From 9568e120f10e35f748cb50bf90f4216ae6d27de4 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Tue, 18 Jun 2024 14:04:28 +0300 Subject: [PATCH] Delegation: support coercion for target expression --- compiler/rustc_ast_lowering/src/delegation.rs | 77 +++++++++++++++---- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 1 + .../src/fn_ctxt/suggestions.rs | 1 + compiler/rustc_hir_typeck/src/method/mod.rs | 15 +++- compiler/rustc_hir_typeck/src/method/probe.rs | 34 ++++++++ .../rustc_hir_typeck/src/method/suggest.rs | 2 + compiler/rustc_resolve/src/late.rs | 22 +++++- tests/ui/delegation/bad-resolve.rs | 3 + tests/ui/delegation/bad-resolve.stderr | 24 +++++- tests/ui/delegation/explicit-paths-pass.rs | 4 +- tests/ui/delegation/explicit-paths.rs | 4 +- tests/ui/delegation/explicit-paths.stderr | 9 ++- tests/ui/delegation/ice-issue-122550.stderr | 25 +++--- tests/ui/delegation/method-call-choice.rs | 25 ++++++ tests/ui/delegation/method-call-choice.stderr | 21 +++++ tests/ui/delegation/method-call-priority.rs | 34 ++++++++ tests/ui/delegation/self-coercion.rs | 26 +++++++ 17 files changed, 291 insertions(+), 36 deletions(-) create mode 100644 tests/ui/delegation/method-call-choice.rs create mode 100644 tests/ui/delegation/method-call-choice.stderr create mode 100644 tests/ui/delegation/method-call-priority.rs create mode 100644 tests/ui/delegation/self-coercion.rs diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 678cac210f413..75b9c2e0de18b 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,74 @@ 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 + // instead of fully qualified call for the self type coercion. fn finalize_body_lowering( &mut self, delegation: &Delegation, args: Vec>, 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, + None, + ); + let segment = self.arena.alloc(segment); + + let method_call_id = self.next_id(); + if let Some(traits) = self.resolver.trait_map.remove(&delegation.id) { + self.trait_map.insert(method_call_id.local_id, traits.into_boxed_slice()); + } + + self.arena.alloc(hir::Expr { + hir_id: method_call_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/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 1138642c56d61..af9a8946f232c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -570,6 +570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), callee_ty.peel_refs(), callee_expr.unwrap().hir_id, + None, TraitsInScope, |mut ctxt| ctxt.probe_for_similar_candidate(), ) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 8d380caf91628..66aee13fffbc8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1588,6 +1588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, expr.hir_id, + None, ProbeScope::TraitsInScope, ) { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index e1223307b53e3..450fa60d03ffb 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -104,6 +104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, call_expr_id, + None, ProbeScope::TraitsInScope, ) { Ok(pick) => { @@ -182,8 +183,13 @@ 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 pick = self.lookup_probe( + segment.ident, + self_ty, + call_expr, + segment.res.opt_def_id(), + ProbeScope::TraitsInScope, + )?; self.lint_edition_dependent_dot_call( self_ty, segment, span, call_expr, self_expr, &pick, args, @@ -208,6 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segment.ident, trait_type, call_expr, + None, ProbeScope::TraitsInScope, ) { Ok(ref new_pick) if pick.differs_from(new_pick) => { @@ -276,6 +283,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method_name: Ident, self_ty: Ty<'tcx>, call_expr: &hir::Expr<'_>, + expected_def_id: Option, scope: ProbeScope, ) -> probe::PickResult<'tcx> { let pick = self.probe_for_name( @@ -285,6 +293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(false), self_ty, call_expr.hir_id, + expected_def_id, scope, )?; pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id); @@ -306,6 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, call_expr.hir_id, + None, scope, )?; Ok(pick) @@ -516,6 +526,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(false), self_ty, expr_id, + None, ProbeScope::TraitsInScope, ); let pick = match (pick, struct_variant) { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9747a91ccbfad..f519366e3803f 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -96,6 +96,27 @@ pub(crate) struct ProbeContext<'a, 'tcx> { scope_expr_id: HirId, + /// Delegation item can be expanded into method calls or fully qualified calls + /// depending on the callee's signature. Method calls are used to allow + /// autoref/autoderef for target expression. For example in: + /// + /// ```ignore (illustrative) + /// 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. The difference with existing method lookup + /// is that methods in delegation items are pre-resolved by callee path (`Trait::*`). + expected_def_id: Option, + /// Is this probe being done for a diagnostic? This will skip some error reporting /// machinery, since we don't particularly care about, for example, similarly named /// candidates if we're *reporting* similarly named candidates. @@ -248,6 +269,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, scope_expr_id, + None, ProbeScope::AllTraits, |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)), ) @@ -263,6 +285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), self_ty, scope_expr_id, + None, ProbeScope::AllTraits, |probe_cx| probe_cx.pick(), ) @@ -281,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: HirId, + expected_def_id: Option, scope: ProbeScope, ) -> PickResult<'tcx> { self.probe_op( @@ -291,6 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion, self_ty, scope_expr_id, + expected_def_id, scope, |probe_cx| probe_cx.pick(), ) @@ -315,6 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion, self_ty, scope_expr_id, + None, scope, |probe_cx| { Ok(probe_cx @@ -335,6 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: HirId, + expected_def_id: Option, scope: ProbeScope, op: OP, ) -> Result> @@ -476,6 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &orig_values, steps.steps, scope_expr_id, + expected_def_id, is_suggestion, ); @@ -571,6 +599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { orig_steps_var_values: &'a OriginalQueryValues<'tcx>, steps: &'tcx [CandidateStep<'tcx>], scope_expr_id: HirId, + expected_def_id: Option, is_suggestion: IsSuggestion, ) -> ProbeContext<'a, 'tcx> { ProbeContext { @@ -590,6 +619,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { static_candidates: RefCell::new(Vec::new()), unsatisfied_predicates: RefCell::new(Vec::new()), scope_expr_id, + expected_def_id, is_suggestion, } } @@ -1220,6 +1250,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ) -> Option> { let mut applicable_candidates: Vec<_> = candidates .iter() + .filter(|candidate| { + !matches!(self.expected_def_id, Some(def_id) if def_id != candidate.item.def_id) + }) .map(|probe| { (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) }) @@ -1677,6 +1710,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.orig_steps_var_values, self.steps, self.scope_expr_id, + None, IsSuggestion(true), ); pcx.allow_similar_names = true; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a385bc70e359b..afdd5b2384d60 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2034,6 +2034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), rcvr_ty, expr_id, + None, ProbeScope::TraitsInScope, ) .is_ok() @@ -3095,6 +3096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), deref_ty, ty.hir_id, + None, ProbeScope::TraitsInScope, ) { if deref_ty.is_suggestable(self.tcx, true) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 66a1c05289b7d..515ce266fd22a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3316,14 +3316,32 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { self.visit_ty(&qself.ty); } self.visit_path(&delegation.path, delegation.id); + let last_ident = delegation.path.segments.last().unwrap().ident; + + // Saving traits for a `MethodCall` that has not yet been generated. + // Traits found in the path are also considered visible: + // + // impl Trait for Type { + // reuse inner::TraitFoo::*; // OK, even `TraitFoo` is not in scope. + // } + let mut traits = self.traits_in_scope(last_ident, ValueNS); + for segment in &delegation.path.segments { + if let Some(partial_res) = self.r.partial_res_map.get(&segment.id) + && let Some(def_id) = partial_res.full_res().and_then(|res| res.opt_def_id()) + && self.r.tcx.def_kind(def_id) == DefKind::Trait + { + traits.push(TraitCandidate { def_id, import_ids: smallvec![] }); + } + } + self.r.trait_map.insert(delegation.id, traits); + if let Some(body) = &delegation.body { self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { // `PatBoundCtx` is not necessary in this context let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - let span = delegation.path.segments.last().unwrap().ident.span; this.fresh_binding( - Ident::new(kw::SelfLower, span), + Ident::new(kw::SelfLower, last_ident.span), delegation.id, PatternSource::FnParam, &mut bindings, diff --git a/tests/ui/delegation/bad-resolve.rs b/tests/ui/delegation/bad-resolve.rs index f378e05304b29..f15e6aa81afb0 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 883ff523bcfea..32d2f3b26cb03 100644 --- a/tests/ui/delegation/bad-resolve.stderr +++ b/tests/ui/delegation/bad-resolve.stderr @@ -25,6 +25,15 @@ LL | reuse ::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 fada793bd118c..dd0ee2c732f59 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 ::static_method; reuse ::static_method2 { S::static_method(self) } } diff --git a/tests/ui/delegation/explicit-paths.rs b/tests/ui/delegation/explicit-paths.rs index a91ca4cb931ed..d42e305b252f0 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 ::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 ::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 d33c5da4377b1..b5afe19f87897 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 ::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 c92170644e78f..1a01bee3e1e27 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 ::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 ::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 0000000000000..8d53d8bfdb72d --- /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 0000000000000..6757af20a6b3a --- /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 0000000000000..8d68740d181a8 --- /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 0000000000000..96c1f1b140b14 --- /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); +}