Skip to content

Commit 64d78b4

Browse files
committed
Delegation: support coercion for target expression
1 parent a4ce33c commit 64d78b4

File tree

15 files changed

+296
-59
lines changed

15 files changed

+296
-59
lines changed

compiler/rustc_ast_lowering/src/delegation.rs

+85-23
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};
@@ -245,12 +245,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
245245
self.lower_body(|this| {
246246
let mut parameters: Vec<hir::Param<'_>> = Vec::with_capacity(param_count);
247247
let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);
248+
let mut target_expr = None;
248249

249250
for idx in 0..param_count {
250251
let (param, pat_node_id) = this.generate_param(span);
251252
parameters.push(param);
252253

253-
let arg = if let Some(block) = block
254+
if let Some(block) = block
254255
&& idx == 0
255256
{
256257
let mut self_resolver = SelfResolver {
@@ -259,44 +260,105 @@ impl<'hir> LoweringContext<'_, 'hir> {
259260
self_param_id: pat_node_id,
260261
};
261262
self_resolver.visit_block(block);
262-
let block = this.lower_block(block, false);
263-
this.mk_expr(hir::ExprKind::Block(block, None), block.span)
263+
let mut block = this.lower_block_noalloc(block, false);
264+
if block.expr.is_none() {
265+
let pat_hir_id = this.lower_node_id(pat_node_id);
266+
let arg = &*this.arena.alloc(this.generate_arg(pat_hir_id, span));
267+
// Use `self` as a first argument if no expression is provided.
268+
block.expr = Some(arg);
269+
}
270+
target_expr = Some(block);
264271
} else {
265272
let pat_hir_id = this.lower_node_id(pat_node_id);
266-
this.generate_arg(pat_hir_id, span)
273+
let arg = this.generate_arg(pat_hir_id, span);
274+
args.push(arg);
267275
};
268-
args.push(arg);
269276
}
270277

271-
let final_expr = this.finalize_body_lowering(delegation, args, span);
278+
let final_expr = this.finalize_body_lowering(delegation, target_expr, args, span);
272279
(this.arena.alloc_from_iter(parameters), final_expr)
273280
})
274281
}
275282

276-
// Generates fully qualified call for the resulting body.
283+
// Generates expression for the resulting body. If possible, `MethodCall` is used
284+
// instead of fully qualified call for the self type coercion.
277285
fn finalize_body_lowering(
278286
&mut self,
279287
delegation: &Delegation,
280-
args: Vec<hir::Expr<'hir>>,
288+
target_expr: Option<hir::Block<'hir>>,
289+
mut args: Vec<hir::Expr<'hir>>,
281290
span: Span,
282291
) -> 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-
292-
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));
292+
let has_generic_args =
293+
delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some());
294+
295+
let (stmts, call, block_id) = if self
296+
.get_resolution_id(delegation.id, span)
297+
.and_then(|def_id| Ok(self.has_self(def_id, span)))
298+
.unwrap_or_default()
299+
&& delegation.qself.is_none()
300+
&& !has_generic_args
301+
{
302+
let ast_segment = delegation.path.segments.last().unwrap();
303+
let segment = self.lower_path_segment(
304+
delegation.path.span,
305+
ast_segment,
306+
ParamMode::Optional,
307+
ParenthesizedGenericArgs::Err,
308+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
309+
None,
310+
None,
311+
);
312+
let segment = self.arena.alloc(segment);
313+
314+
let args = &*self.arena.alloc_from_iter(args);
315+
let (stmts, receiver, args, block_id) = if let Some(target_expr) = target_expr {
316+
let receiver = target_expr.expr.unwrap();
317+
(Some(target_expr.stmts), receiver, args, target_expr.hir_id)
318+
} else {
319+
(None, &args[0], &args[1..], self.next_id())
320+
};
321+
322+
let method_call_id = self.next_id();
323+
if let Some(traits) = self.resolver.trait_map.remove(&delegation.id) {
324+
self.trait_map.insert(method_call_id.local_id, traits.into_boxed_slice());
325+
}
295326

327+
let method_call = self.arena.alloc(hir::Expr {
328+
hir_id: method_call_id,
329+
kind: hir::ExprKind::MethodCall(segment, receiver, args, span),
330+
span,
331+
});
332+
333+
(stmts, method_call, block_id)
334+
} else {
335+
let path = self.lower_qpath(
336+
delegation.id,
337+
&delegation.qself,
338+
&delegation.path,
339+
ParamMode::Optional,
340+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
341+
None,
342+
);
343+
344+
if let Some(target_expr) = target_expr {
345+
let target_expr = self.arena.alloc(target_expr);
346+
let fst_arg =
347+
self.mk_expr(hir::ExprKind::Block(target_expr, None), target_expr.span);
348+
args.insert(0, fst_arg);
349+
}
350+
351+
let args = self.arena.alloc_from_iter(args);
352+
let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
353+
354+
let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span));
355+
356+
(None, call, self.next_id())
357+
};
296358
let block = self.arena.alloc(hir::Block {
297-
stmts: &[],
359+
stmts: stmts.unwrap_or(&[]),
298360
expr: Some(call),
299-
hir_id: self.next_id(),
361+
hir_id: block_id,
300362
rules: hir::BlockCheckMode::DefaultBlock,
301363
span,
302364
targeted_by_break: false,

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+1
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
570570
IsSuggestion(true),
571571
callee_ty.peel_refs(),
572572
callee_expr.unwrap().hir_id,
573+
None,
573574
TraitsInScope,
574575
|mut ctxt| ctxt.probe_for_similar_candidate(),
575576
)

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1588,6 +1588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15881588
IsSuggestion(true),
15891589
self_ty,
15901590
expr.hir_id,
1591+
None,
15911592
ProbeScope::TraitsInScope,
15921593
)
15931594
{

compiler/rustc_hir_typeck/src/method/mod.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
104104
IsSuggestion(true),
105105
self_ty,
106106
call_expr_id,
107+
None,
107108
ProbeScope::TraitsInScope,
108109
) {
109110
Ok(pick) => {
@@ -182,8 +183,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
182183
self_expr: &'tcx hir::Expr<'tcx>,
183184
args: &'tcx [hir::Expr<'tcx>],
184185
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
185-
let pick =
186-
self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
186+
let pick = self.lookup_probe(
187+
segment.ident,
188+
self_ty,
189+
call_expr,
190+
segment.res.opt_def_id(),
191+
ProbeScope::TraitsInScope,
192+
)?;
187193

188194
self.lint_edition_dependent_dot_call(
189195
self_ty, segment, span, call_expr, self_expr, &pick, args,
@@ -208,6 +214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
208214
segment.ident,
209215
trait_type,
210216
call_expr,
217+
None,
211218
ProbeScope::TraitsInScope,
212219
) {
213220
Ok(ref new_pick) if pick.differs_from(new_pick) => {
@@ -276,6 +283,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
276283
method_name: Ident,
277284
self_ty: Ty<'tcx>,
278285
call_expr: &hir::Expr<'_>,
286+
pre_resolved_method: Option<DefId>,
279287
scope: ProbeScope,
280288
) -> probe::PickResult<'tcx> {
281289
let pick = self.probe_for_name(
@@ -285,6 +293,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
285293
IsSuggestion(false),
286294
self_ty,
287295
call_expr.hir_id,
296+
pre_resolved_method,
288297
scope,
289298
)?;
290299
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> {
306315
IsSuggestion(true),
307316
self_ty,
308317
call_expr.hir_id,
318+
None,
309319
scope,
310320
)?;
311321
Ok(pick)
@@ -516,6 +526,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
516526
IsSuggestion(false),
517527
self_ty,
518528
expr_id,
529+
None,
519530
ProbeScope::TraitsInScope,
520531
);
521532
let pick = match (pick, struct_variant) {

compiler/rustc_hir_typeck/src/method/probe.rs

+39
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,27 @@ pub(crate) struct ProbeContext<'a, 'tcx> {
9696

9797
scope_expr_id: HirId,
9898

99+
/// Delegation item can be expanded into method calls or fully qualified calls
100+
/// depending on the callee's signature. Method calls are used to allow
101+
/// autoref/autoderef for target expression. For example in:
102+
///
103+
/// ```ignore (illustrative)
104+
/// trait Trait : Sized {
105+
/// fn by_value(self) -> i32 { 1 }
106+
/// fn by_mut_ref(&mut self) -> i32 { 2 }
107+
/// fn by_ref(&self) -> i32 { 3 }
108+
/// }
109+
///
110+
/// struct NewType(SomeType);
111+
/// impl Trait for NewType {
112+
/// reuse Trait::* { self.0 }
113+
/// }
114+
/// ```
115+
///
116+
/// `self.0` will automatically coerce. The difference with existing method lookup
117+
/// is that methods in delegation items are pre-resolved by callee path (`Trait::*`).
118+
pre_resolved_method: Option<DefId>,
119+
99120
/// Is this probe being done for a diagnostic? This will skip some error reporting
100121
/// machinery, since we don't particularly care about, for example, similarly named
101122
/// candidates if we're *reporting* similarly named candidates.
@@ -248,6 +269,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
248269
IsSuggestion(true),
249270
self_ty,
250271
scope_expr_id,
272+
None,
251273
ProbeScope::AllTraits,
252274
|probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)),
253275
)
@@ -263,6 +285,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
263285
IsSuggestion(true),
264286
self_ty,
265287
scope_expr_id,
288+
None,
266289
ProbeScope::AllTraits,
267290
|probe_cx| probe_cx.pick(),
268291
)
@@ -281,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
281304
is_suggestion: IsSuggestion,
282305
self_ty: Ty<'tcx>,
283306
scope_expr_id: HirId,
307+
pre_resolved_method: Option<DefId>,
284308
scope: ProbeScope,
285309
) -> PickResult<'tcx> {
286310
self.probe_op(
@@ -291,6 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
291315
is_suggestion,
292316
self_ty,
293317
scope_expr_id,
318+
pre_resolved_method,
294319
scope,
295320
|probe_cx| probe_cx.pick(),
296321
)
@@ -315,6 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
315340
is_suggestion,
316341
self_ty,
317342
scope_expr_id,
343+
None,
318344
scope,
319345
|probe_cx| {
320346
Ok(probe_cx
@@ -335,6 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
335361
is_suggestion: IsSuggestion,
336362
self_ty: Ty<'tcx>,
337363
scope_expr_id: HirId,
364+
pre_resolved_method: Option<DefId>,
338365
scope: ProbeScope,
339366
op: OP,
340367
) -> Result<R, MethodError<'tcx>>
@@ -476,6 +503,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
476503
&orig_values,
477504
steps.steps,
478505
scope_expr_id,
506+
pre_resolved_method,
479507
is_suggestion,
480508
);
481509

@@ -571,6 +599,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
571599
orig_steps_var_values: &'a OriginalQueryValues<'tcx>,
572600
steps: &'tcx [CandidateStep<'tcx>],
573601
scope_expr_id: HirId,
602+
pre_resolved_method: Option<DefId>,
574603
is_suggestion: IsSuggestion,
575604
) -> ProbeContext<'a, 'tcx> {
576605
ProbeContext {
@@ -590,6 +619,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
590619
static_candidates: RefCell::new(Vec::new()),
591620
unsatisfied_predicates: RefCell::new(Vec::new()),
592621
scope_expr_id,
622+
pre_resolved_method,
593623
is_suggestion,
594624
}
595625
}
@@ -1220,6 +1250,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12201250
) -> Option<PickResult<'tcx>> {
12211251
let mut applicable_candidates: Vec<_> = candidates
12221252
.iter()
1253+
.filter(|candidate| {
1254+
if let Some(res_id) = self.pre_resolved_method
1255+
&& candidate.item.def_id != res_id
1256+
{
1257+
return false;
1258+
}
1259+
true
1260+
})
12231261
.map(|probe| {
12241262
(probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates))
12251263
})
@@ -1677,6 +1715,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
16771715
self.orig_steps_var_values,
16781716
self.steps,
16791717
self.scope_expr_id,
1718+
None,
16801719
IsSuggestion(true),
16811720
);
16821721
pcx.allow_similar_names = true;

compiler/rustc_hir_typeck/src/method/suggest.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2034,6 +2034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20342034
IsSuggestion(true),
20352035
rcvr_ty,
20362036
expr_id,
2037+
None,
20372038
ProbeScope::TraitsInScope,
20382039
)
20392040
.is_ok()
@@ -3095,6 +3096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30953096
IsSuggestion(true),
30963097
deref_ty,
30973098
ty.hir_id,
3099+
None,
30983100
ProbeScope::TraitsInScope,
30993101
) {
31003102
if deref_ty.is_suggestable(self.tcx, true)

0 commit comments

Comments
 (0)