Skip to content

Commit 7ee97f9

Browse files
committed
Delegation: support coercion for target expression
1 parent 5572759 commit 7ee97f9

13 files changed

+264
-37
lines changed

compiler/rustc_ast_lowering/src/delegation.rs

+69-15
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
3939
use crate::{ImplTraitPosition, ResolverAstLoweringExt};
4040

41-
use super::{ImplTraitContext, LoweringContext, ParamMode};
41+
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
4242

4343
use ast::visit::Visitor;
4444
use hir::def::{DefKind, PartialRes, Res};
@@ -259,8 +259,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
259259
self_param_id: pat_node_id,
260260
};
261261
self_resolver.visit_block(block);
262-
let block = this.lower_block(block, false);
263-
this.mk_expr(hir::ExprKind::Block(block, None), block.span)
262+
this.lower_target_expr(&block)
264263
} else {
265264
let pat_hir_id = this.lower_node_id(pat_node_id);
266265
this.generate_arg(pat_hir_id, span)
@@ -273,26 +272,81 @@ impl<'hir> LoweringContext<'_, 'hir> {
273272
})
274273
}
275274

276-
// Generates fully qualified call for the resulting body.
275+
// FIXME(fn_delegation): Alternatives for target expression lowering:
276+
// https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600.
277+
fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> {
278+
if block.stmts.len() == 1
279+
&& let StmtKind::Expr(expr) = &block.stmts[0].kind
280+
{
281+
return self.lower_expr_mut(expr);
282+
}
283+
284+
let block = self.lower_block(block, false);
285+
self.mk_expr(hir::ExprKind::Block(block, None), block.span)
286+
}
287+
288+
// Generates expression for the resulting body. If possible, `MethodCall` is used
289+
// to allow autoref/autoderef for target expression. For example in:
290+
//
291+
// trait Trait : Sized {
292+
// fn by_value(self) -> i32 { 1 }
293+
// fn by_mut_ref(&mut self) -> i32 { 2 }
294+
// fn by_ref(&self) -> i32 { 3 }
295+
// }
296+
//
297+
// struct NewType(SomeType);
298+
// impl Trait for NewType {
299+
// reuse Trait::* { self.0 }
300+
// }
301+
//
302+
// `self.0` will automatically coerce.
277303
fn finalize_body_lowering(
278304
&mut self,
279305
delegation: &Delegation,
280306
args: Vec<hir::Expr<'hir>>,
281307
span: Span,
282308
) -> hir::Expr<'hir> {
283-
let path = self.lower_qpath(
284-
delegation.id,
285-
&delegation.qself,
286-
&delegation.path,
287-
ParamMode::Optional,
288-
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
289-
None,
290-
);
291-
292309
let args = self.arena.alloc_from_iter(args);
293-
let path_expr = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
294-
let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(path_expr, args), span));
295310

311+
let has_generic_args =
312+
delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some());
313+
314+
let call = if self
315+
.get_resolution_id(delegation.id, span)
316+
.and_then(|def_id| Ok(self.has_self(def_id, span)))
317+
.unwrap_or_default()
318+
&& delegation.qself.is_none()
319+
&& !has_generic_args
320+
{
321+
let ast_segment = delegation.path.segments.last().unwrap();
322+
let segment = self.lower_path_segment(
323+
delegation.path.span,
324+
ast_segment,
325+
ParamMode::Optional,
326+
ParenthesizedGenericArgs::Err,
327+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
328+
None,
329+
);
330+
let segment = self.arena.alloc(segment);
331+
332+
self.arena.alloc(hir::Expr {
333+
hir_id: self.next_id(),
334+
kind: hir::ExprKind::MethodCall(segment, &args[0], &args[1..], span),
335+
span,
336+
})
337+
} else {
338+
let path = self.lower_qpath(
339+
delegation.id,
340+
&delegation.qself,
341+
&delegation.path,
342+
ParamMode::Optional,
343+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
344+
None,
345+
);
346+
347+
let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
348+
self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span))
349+
};
296350
let block = self.arena.alloc(hir::Block {
297351
stmts: &[],
298352
expr: Some(call),

compiler/rustc_hir_typeck/src/method/mod.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
182182
self_expr: &'tcx hir::Expr<'tcx>,
183183
args: &'tcx [hir::Expr<'tcx>],
184184
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
185-
let pick =
186-
self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
185+
let scope = if let Some(only_method) = segment.res.opt_def_id() {
186+
ProbeScope::Single(only_method)
187+
} else {
188+
ProbeScope::TraitsInScope
189+
};
190+
191+
let pick = self.lookup_probe(segment.ident, self_ty, call_expr, scope)?;
187192

188193
self.lint_edition_dependent_dot_call(
189194
self_ty, segment, span, call_expr, self_expr, &pick, args,

compiler/rustc_hir_typeck/src/method/probe.rs

+30-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_middle::middle::stability;
2020
use rustc_middle::query::Providers;
2121
use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
2222
use rustc_middle::ty::AssocItem;
23+
use rustc_middle::ty::AssocItemContainer;
2324
use rustc_middle::ty::GenericParamDefKind;
2425
use rustc_middle::ty::Upcast;
2526
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
@@ -216,6 +217,9 @@ pub enum Mode {
216217

217218
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
218219
pub enum ProbeScope {
220+
// Single candidate coming from pre-resolved delegation method.
221+
Single(DefId),
222+
219223
// Assemble candidates coming only from traits in scope.
220224
TraitsInScope,
221225

@@ -480,12 +484,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
480484
is_suggestion,
481485
);
482486

483-
probe_cx.assemble_inherent_candidates();
484487
match scope {
485488
ProbeScope::TraitsInScope => {
486-
probe_cx.assemble_extension_candidates_for_traits_in_scope()
489+
probe_cx.assemble_inherent_candidates();
490+
probe_cx.assemble_extension_candidates_for_traits_in_scope();
491+
}
492+
ProbeScope::AllTraits => {
493+
probe_cx.assemble_inherent_candidates();
494+
probe_cx.assemble_extension_candidates_for_all_traits();
495+
}
496+
ProbeScope::Single(def_id) => {
497+
let item = self.tcx.associated_item(def_id);
498+
// FIXME(fn_delegation): Delegation to inherent methods is not yet supported.
499+
assert_eq!(item.container, AssocItemContainer::TraitContainer);
500+
501+
let trait_def_id = self.tcx.parent(def_id);
502+
let trait_span = self.tcx.def_span(trait_def_id);
503+
504+
let trait_args = self.fresh_args_for_item(trait_span, trait_def_id);
505+
let trait_ref = ty::TraitRef::new_from_args(self.tcx, trait_def_id, trait_args);
506+
507+
probe_cx.push_candidate(
508+
Candidate {
509+
item,
510+
kind: CandidateKind::TraitCandidate(ty::Binder::dummy(trait_ref)),
511+
import_ids: smallvec![],
512+
},
513+
false,
514+
);
487515
}
488-
ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
489516
};
490517
op(probe_cx)
491518
})

tests/ui/delegation/bad-resolve.rs

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ impl Trait for S {
3434

3535
reuse foo { &self.0 }
3636
//~^ ERROR cannot find function `foo` in this scope
37+
reuse Trait::foo2 { self.0 }
38+
//~^ ERROR cannot find function `foo2` in trait `Trait`
39+
//~| ERROR method `foo2` is not a member of trait `Trait`
3740
}
3841

3942
mod prefix {}

tests/ui/delegation/bad-resolve.stderr

+21-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ LL | reuse <F as Trait>::baz;
2525
| | help: there is an associated function with a similar name: `bar`
2626
| not a member of trait `Trait`
2727

28+
error[E0407]: method `foo2` is not a member of trait `Trait`
29+
--> $DIR/bad-resolve.rs:37:5
30+
|
31+
LL | reuse Trait::foo2 { self.0 }
32+
| ^^^^^^^^^^^^^----^^^^^^^^^^^
33+
| | |
34+
| | help: there is an associated function with a similar name: `foo`
35+
| not a member of trait `Trait`
36+
2837
error[E0423]: expected function, found associated constant `Trait::C`
2938
--> $DIR/bad-resolve.rs:24:11
3039
|
@@ -54,6 +63,15 @@ error[E0425]: cannot find function `foo` in this scope
5463
LL | reuse foo { &self.0 }
5564
| ^^^ not found in this scope
5665

66+
error[E0425]: cannot find function `foo2` in trait `Trait`
67+
--> $DIR/bad-resolve.rs:37:18
68+
|
69+
LL | fn foo(&self, x: i32) -> i32 { x }
70+
| ---------------------------- similarly named associated function `foo` defined here
71+
...
72+
LL | reuse Trait::foo2 { self.0 }
73+
| ^^^^ help: an associated function with a similar name exists: `foo`
74+
5775
error[E0046]: not all trait items implemented, missing: `Type`
5876
--> $DIR/bad-resolve.rs:22:1
5977
|
@@ -64,18 +82,18 @@ LL | impl Trait for S {
6482
| ^^^^^^^^^^^^^^^^ missing `Type` in implementation
6583

6684
error[E0433]: failed to resolve: use of undeclared crate or module `unresolved_prefix`
67-
--> $DIR/bad-resolve.rs:40:7
85+
--> $DIR/bad-resolve.rs:43:7
6886
|
6987
LL | reuse unresolved_prefix::{a, b, c};
7088
| ^^^^^^^^^^^^^^^^^ use of undeclared crate or module `unresolved_prefix`
7189

7290
error[E0433]: failed to resolve: `crate` in paths can only be used in start position
73-
--> $DIR/bad-resolve.rs:41:29
91+
--> $DIR/bad-resolve.rs:44:29
7492
|
7593
LL | reuse prefix::{self, super, crate};
7694
| ^^^^^ `crate` in paths can only be used in start position
7795

78-
error: aborting due to 10 previous errors
96+
error: aborting due to 12 previous errors
7997

8098
Some errors have detailed explanations: E0046, E0324, E0407, E0423, E0425, E0433, E0575, E0576.
8199
For more information about an error, try `rustc --explain E0046`.

tests/ui/delegation/explicit-paths-pass.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ reuse to_reuse::zero_args { self }
2424

2525
struct S(F);
2626
impl Trait for S {
27-
reuse Trait::bar { &self.0 }
28-
reuse Trait::description { &self.0 }
27+
reuse Trait::bar { self.0 }
28+
reuse Trait::description { self.0 }
2929
reuse <F as Trait>::static_method;
3030
reuse <F as Trait>::static_method2 { S::static_method(self) }
3131
}

tests/ui/delegation/explicit-paths.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ mod inherent_impl_assoc_fn_to_other {
3434
use crate::*;
3535

3636
impl S {
37-
reuse Trait::foo1 { &self.0 }
37+
reuse Trait::foo1 { self.0 }
3838
reuse <S as Trait>::foo2;
3939
reuse to_reuse::foo3;
4040
reuse F::foo4 { &self.0 }
@@ -46,7 +46,7 @@ mod trait_impl_assoc_fn_to_other {
4646
use crate::*;
4747

4848
impl Trait for S {
49-
reuse Trait::foo1 { &self.0 }
49+
reuse Trait::foo1 { self.0 }
5050
reuse <F as Trait>::foo2;
5151
reuse to_reuse::foo3;
5252
//~^ ERROR method `foo3` is not a member of trait `Trait`

tests/ui/delegation/explicit-paths.stderr

+8-1
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,17 @@ error[E0308]: mismatched types
9191
LL | trait Trait2 : Trait {
9292
| -------------------- found this type parameter
9393
LL | reuse <F as Trait>::foo1 { self }
94-
| ^^^^ expected `&F`, found `&Self`
94+
| ---- ^^^^ expected `&F`, found `&Self`
95+
| |
96+
| arguments to this function are incorrect
9597
|
9698
= note: expected reference `&F`
9799
found reference `&Self`
100+
note: method defined here
101+
--> $DIR/explicit-paths.rs:5:8
102+
|
103+
LL | fn foo1(&self, x: i32) -> i32 { x }
104+
| ^^^^ -----
98105

99106
error[E0277]: the trait bound `S2: Trait` is not satisfied
100107
--> $DIR/explicit-paths.rs:78:16

tests/ui/delegation/ice-issue-122550.stderr

+16-9
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,6 @@ error[E0308]: mismatched types
44
LL | fn description(&self) -> &str {}
55
| ^^ expected `&str`, found `()`
66

7-
error[E0308]: mismatched types
8-
--> $DIR/ice-issue-122550.rs:13:39
9-
|
10-
LL | reuse <S as Trait>::description { &self.0 }
11-
| ^^^^^^^ expected `&S`, found `&F`
12-
|
13-
= note: expected reference `&S`
14-
found reference `&F`
15-
167
error[E0277]: the trait bound `S: Trait` is not satisfied
178
--> $DIR/ice-issue-122550.rs:13:12
189
|
@@ -25,6 +16,22 @@ help: this trait has no implementations, consider adding one
2516
LL | trait Trait {
2617
| ^^^^^^^^^^^
2718

19+
error[E0308]: mismatched types
20+
--> $DIR/ice-issue-122550.rs:13:39
21+
|
22+
LL | reuse <S as Trait>::description { &self.0 }
23+
| ----------- ^^^^^^^ expected `&S`, found `&F`
24+
| |
25+
| arguments to this function are incorrect
26+
|
27+
= note: expected reference `&S`
28+
found reference `&F`
29+
note: method defined here
30+
--> $DIR/ice-issue-122550.rs:5:8
31+
|
32+
LL | fn description(&self) -> &str {}
33+
| ^^^^^^^^^^^ -----
34+
2835
error: aborting due to 3 previous errors
2936

3037
Some errors have detailed explanations: E0277, E0308.
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![feature(fn_delegation)]
2+
#![allow(incomplete_features)]
3+
4+
trait Trait {
5+
fn foo(&self) {}
6+
}
7+
8+
struct F;
9+
impl Trait for F {}
10+
struct S(F);
11+
12+
pub mod to_reuse {
13+
use crate::F;
14+
15+
pub fn foo(_: &F) {}
16+
}
17+
18+
impl Trait for S {
19+
// Make sure that the method call is not generated if the path resolution
20+
// does not have a `self` parameter.
21+
reuse to_reuse::foo { self.0 }
22+
//~^ ERROR mismatched types
23+
}
24+
25+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/method-call-choice.rs:21:27
3+
|
4+
LL | reuse to_reuse::foo { self.0 }
5+
| --- ^^^^^^ expected `&F`, found `F`
6+
| |
7+
| arguments to this function are incorrect
8+
|
9+
note: function defined here
10+
--> $DIR/method-call-choice.rs:15:12
11+
|
12+
LL | pub fn foo(_: &F) {}
13+
| ^^^ -----
14+
help: consider borrowing here
15+
|
16+
LL | reuse to_reuse::foo { &self.0 }
17+
| +
18+
19+
error: aborting due to 1 previous error
20+
21+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)