Skip to content

Commit de488f2

Browse files
committed
Delegation: support coercion for target expression
1 parent 06194ca commit de488f2

File tree

13 files changed

+269
-86
lines changed

13 files changed

+269
-86
lines changed

compiler/rustc_ast_lowering/src/delegation.rs

+117-72
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};
@@ -66,15 +66,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
6666
let Ok(sig_id) = sig_id else {
6767
return false;
6868
};
69-
if let Some(local_sig_id) = sig_id.as_local() {
70-
self.resolver.delegation_fn_sigs[&local_sig_id].has_self
71-
} else {
72-
match self.tcx.def_kind(sig_id) {
73-
DefKind::Fn => false,
74-
DefKind::AssocFn => self.tcx.associated_item(sig_id).fn_has_self_parameter,
75-
_ => span_bug!(span, "unexpected DefKind for delegation item"),
76-
}
77-
}
69+
self.has_self(sig_id, span)
7870
}
7971

8072
pub(crate) fn lower_delegation(
@@ -105,12 +97,29 @@ impl<'hir> LoweringContext<'_, 'hir> {
10597
span: Span,
10698
) -> Result<DefId, ErrorGuaranteed> {
10799
let sig_id = if self.is_in_trait_impl { item_id } else { path_id };
108-
let sig_id =
109-
self.resolver.get_partial_res(sig_id).and_then(|r| r.expect_full_res().opt_def_id());
110-
sig_id.ok_or_else(|| {
111-
self.tcx
112-
.dcx()
113-
.span_delayed_bug(span, "LoweringContext: couldn't resolve delegation item")
100+
self.get_resolution_id(sig_id, span)
101+
}
102+
103+
fn has_self(&self, def_id: DefId, span: Span) -> bool {
104+
if let Some(local_sig_id) = def_id.as_local() {
105+
self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self)
106+
} else {
107+
match self.tcx.def_kind(def_id) {
108+
DefKind::Fn => false,
109+
DefKind::AssocFn => self.tcx.associated_item(def_id).fn_has_self_parameter,
110+
_ => span_bug!(span, "unexpected DefKind for delegation item"),
111+
}
112+
}
113+
}
114+
115+
fn get_resolution_id(&self, node_id: NodeId, span: Span) -> Result<DefId, ErrorGuaranteed> {
116+
let def_id =
117+
self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id());
118+
def_id.ok_or_else(|| {
119+
self.tcx.dcx().span_delayed_bug(
120+
span,
121+
format!("LoweringContext: couldn't resolve node {:?} in delegation item", node_id),
122+
)
114123
})
115124
}
116125

@@ -120,7 +129,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
120129
predicates: &[],
121130
has_where_clause_predicates: false,
122131
where_clause_span: span,
123-
span: span,
132+
span,
124133
})
125134
}
126135

@@ -220,12 +229,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
220229
}));
221230

222231
let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments });
223-
224-
hir::Expr {
225-
hir_id: self.next_id(),
226-
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
227-
span,
228-
}
232+
self.mk_expr(hir::ExprKind::Path(hir::QPath::Resolved(None, path)), span)
229233
}
230234

231235
fn lower_delegation_body(
@@ -234,25 +238,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
234238
param_count: usize,
235239
span: Span,
236240
) -> BodyId {
237-
let path = self.lower_qpath(
238-
delegation.id,
239-
&delegation.qself,
240-
&delegation.path,
241-
ParamMode::Optional,
242-
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
243-
None,
244-
);
245241
let block = delegation.body.as_deref();
246242

247243
self.lower_body(|this| {
248-
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
249-
let mut args: Vec<hir::Expr<'hir>> = Vec::new();
244+
let mut parameters: Vec<hir::Param<'_>> = Vec::with_capacity(param_count);
245+
let mut args: Vec<hir::Expr<'_>> = Vec::with_capacity(param_count);
246+
let mut target_expr = None;
250247

251248
for idx in 0..param_count {
252249
let (param, pat_node_id) = this.generate_param(span);
253250
parameters.push(param);
254251

255-
let arg = if let Some(block) = block
252+
if let Some(block) = block
256253
&& idx == 0
257254
{
258255
let mut self_resolver = SelfResolver {
@@ -261,56 +258,103 @@ impl<'hir> LoweringContext<'_, 'hir> {
261258
self_param_id: pat_node_id,
262259
};
263260
self_resolver.visit_block(block);
264-
let block = this.lower_block(block, false);
265-
hir::Expr {
266-
hir_id: this.next_id(),
267-
kind: hir::ExprKind::Block(block, None),
268-
span: block.span,
269-
}
261+
target_expr = Some(this.lower_block_noalloc(block, false));
270262
} else {
271263
let pat_hir_id = this.lower_node_id(pat_node_id);
272-
this.generate_arg(pat_hir_id, span)
264+
let arg = this.generate_arg(pat_hir_id, span);
265+
args.push(arg);
273266
};
274-
args.push(arg);
275267
}
276268

277-
let args = self.arena.alloc_from_iter(args);
278-
let final_expr = this.generate_call(path, args);
269+
let final_expr = this.finalize_body_lowering(delegation, target_expr, args, span);
279270
(this.arena.alloc_from_iter(parameters), final_expr)
280271
})
281272
}
282273

283-
fn generate_call(
274+
// Generates expression for the resulting body. If possible, `MethodCall` is used
275+
// instead of fully qualified call for the self type coercion. See `consider_candidates`
276+
// for more information.
277+
fn finalize_body_lowering(
284278
&mut self,
285-
path: hir::QPath<'hir>,
286-
args: &'hir [hir::Expr<'hir>],
279+
delegation: &Delegation,
280+
target_expr: Option<hir::Block<'hir>>,
281+
mut args: Vec<hir::Expr<'hir>>,
282+
span: Span,
287283
) -> hir::Expr<'hir> {
288-
let callee = self.arena.alloc(hir::Expr {
289-
hir_id: self.next_id(),
290-
kind: hir::ExprKind::Path(path),
291-
span: path.span(),
292-
});
284+
let (stmts, call, block_id) = if self
285+
.get_resolution_id(delegation.id, span)
286+
.and_then(|def_id| Ok(self.has_self(def_id, span)))
287+
.unwrap_or_default()
288+
&& delegation.qself.is_none()
289+
{
290+
let ast_segment = delegation.path.segments.last().unwrap();
291+
let segment = self.lower_path_segment(
292+
delegation.path.span,
293+
ast_segment,
294+
ParamMode::Optional,
295+
ParenthesizedGenericArgs::Err,
296+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
297+
None,
298+
None,
299+
);
300+
let segment = self.arena.alloc(segment);
301+
302+
let args = &*self.arena.alloc_from_iter(args);
303+
let (stmts, receiver, args, block_id) = if let Some(target_expr) = target_expr {
304+
// Use unit type as a receiver if no expression is provided.
305+
let receiver = target_expr.expr.unwrap_or_else(|| {
306+
self.arena.alloc(self.mk_expr(hir::ExprKind::Tup(&[]), span))
307+
});
308+
(target_expr.stmts, receiver, args, target_expr.hir_id)
309+
} else {
310+
(&*self.arena.alloc_from_iter([]), &args[0], &args[1..], self.next_id())
311+
};
293312

294-
let expr = self.arena.alloc(hir::Expr {
295-
hir_id: self.next_id(),
296-
kind: hir::ExprKind::Call(callee, args),
297-
span: path.span(),
298-
});
313+
let method_call_id = self.next_id();
314+
if let Some(traits) = self.resolver.delegation_trait_map.remove(&delegation.id) {
315+
self.trait_map.insert(method_call_id.local_id, traits.into_boxed_slice());
316+
}
317+
318+
let method_call = self.arena.alloc(hir::Expr {
319+
hir_id: method_call_id,
320+
kind: hir::ExprKind::MethodCall(segment, receiver, args, span),
321+
span,
322+
});
323+
324+
(stmts, method_call, block_id)
325+
} else {
326+
let path = self.lower_qpath(
327+
delegation.id,
328+
&delegation.qself,
329+
&delegation.path,
330+
ParamMode::Optional,
331+
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
332+
None,
333+
);
334+
335+
if let Some(target_expr) = target_expr {
336+
let target_expr = self.arena.alloc(target_expr);
337+
let fst_arg =
338+
self.mk_expr(hir::ExprKind::Block(target_expr, None), target_expr.span);
339+
args.insert(0, fst_arg);
340+
}
299341

342+
let args = self.arena.alloc_from_iter(args);
343+
let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
344+
345+
let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span));
346+
347+
(&*self.arena.alloc_from_iter([]), call, self.next_id())
348+
};
300349
let block = self.arena.alloc(hir::Block {
301-
stmts: &[],
302-
expr: Some(expr),
303-
hir_id: self.next_id(),
350+
stmts,
351+
expr: Some(call),
352+
hir_id: block_id,
304353
rules: hir::BlockCheckMode::DefaultBlock,
305-
span: path.span(),
354+
span,
306355
targeted_by_break: false,
307356
});
308-
309-
hir::Expr {
310-
hir_id: self.next_id(),
311-
kind: hir::ExprKind::Block(block, None),
312-
span: path.span(),
313-
}
357+
self.mk_expr(hir::ExprKind::Block(block, None), span)
314358
}
315359

316360
fn generate_delegation_error(
@@ -331,11 +375,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
331375
let header = self.generate_header_error();
332376
let sig = hir::FnSig { decl, header, span };
333377

334-
let body_id = self.lower_body(|this| {
335-
let expr =
336-
hir::Expr { hir_id: this.next_id(), kind: hir::ExprKind::Err(err), span: span };
337-
(&[], expr)
338-
});
378+
let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span)));
339379
DelegationResults { generics, body_id, sig }
340380
}
341381

@@ -347,6 +387,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
347387
abi: abi::Abi::Rust,
348388
}
349389
}
390+
391+
#[inline]
392+
fn mk_expr(&mut self, kind: hir::ExprKind<'hir>, span: Span) -> hir::Expr<'hir> {
393+
hir::Expr { hir_id: self.next_id(), kind, span }
394+
}
350395
}
351396

352397
struct SelfResolver<'a> {

compiler/rustc_hir_typeck/src/method/probe.rs

+35
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir as hir;
1010
use rustc_hir::def::DefKind;
1111
use rustc_hir::HirId;
1212
use rustc_hir_analysis::autoderef::{self, Autoderef};
13+
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
1314
use rustc_infer::infer::canonical::OriginalQueryValues;
1415
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
1516
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
@@ -1218,8 +1219,42 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12181219
)>,
12191220
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
12201221
) -> Option<PickResult<'tcx>> {
1222+
let item_id = self.fcx.item_def_id();
1223+
let is_delegation = self.fcx.tcx.hir().opt_delegation_sig_id(item_id);
12211224
let mut applicable_candidates: Vec<_> = candidates
12221225
.iter()
1226+
.filter(|candidate| {
1227+
// Delegation item can be expanded into method calls or fully qualified calls
1228+
// depending on the callee's signature. Method calls are used to allow
1229+
// autoref/autoderef for target expression. For example in:
1230+
//
1231+
// trait Trait : Sized {
1232+
// fn by_value(self) -> i32 { 1 }
1233+
// fn by_mut_ref(&mut self) -> i32 { 2 }
1234+
// fn by_ref(&self) -> i32 { 3 }
1235+
// }
1236+
//
1237+
// struct NewType(SomeType);
1238+
// impl Trait for NewType {
1239+
// reuse Trait::* { self.0 }
1240+
// }
1241+
//
1242+
// `self.0` will automatically coerce. The difference with existing method lookup
1243+
// is that methods in delegation items are pre-resolved by callee path (`Trait::*`).
1244+
// Therefore, we are only forced to consider the pre-resolved method here.
1245+
if let Some(sig_id) = is_delegation {
1246+
let body = self.fcx.tcx.hir().body_owned_by(self.fcx.body_id);
1247+
let generated_method_id = match body.value.kind {
1248+
hir::ExprKind::Block(block, _) => block.expr.unwrap().hir_id,
1249+
_ => unreachable!(),
1250+
};
1251+
if generated_method_id == self.scope_expr_id && candidate.item.def_id != sig_id
1252+
{
1253+
return false;
1254+
}
1255+
}
1256+
true
1257+
})
12231258
.map(|probe| {
12241259
(probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates))
12251260
})

compiler/rustc_middle/src/hir/map/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,21 @@ impl<'hir> Map<'hir> {
728728
}
729729
}
730730

731+
pub fn opt_delegation_sig_id(self, def_id: LocalDefId) -> Option<DefId> {
732+
if let Some(ret) = self.get_fn_output(def_id)
733+
&& let FnRetTy::Return(ty) = ret
734+
&& let TyKind::InferDelegation(sig_id, _) = ty.kind
735+
{
736+
return Some(sig_id);
737+
}
738+
None
739+
}
740+
741+
#[inline]
742+
pub fn delegation_sig_id(self, def_id: LocalDefId) -> DefId {
743+
self.opt_delegation_sig_id(def_id).unwrap()
744+
}
745+
731746
#[inline]
732747
fn opt_ident(self, id: HirId) -> Option<Ident> {
733748
match self.tcx.hir_node(id) {

compiler/rustc_middle/src/ty/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,10 @@ pub struct ResolverAstLowering {
225225
/// Lints that were emitted by the resolver and early lints.
226226
pub lint_buffer: Steal<LintBuffer>,
227227

228-
/// Information about functions signatures for delegation items expansion
228+
/// Information about functions signatures for delegation items expansion.
229229
pub delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
230+
/// Separate `trait_map` for delegation items only.
231+
pub delegation_trait_map: NodeMap<Vec<hir::TraitCandidate>>,
230232
}
231233

232234
#[derive(Debug)]

compiler/rustc_resolve/src/late.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -3279,14 +3279,18 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
32793279
if let Some(qself) = &delegation.qself {
32803280
self.visit_ty(&qself.ty);
32813281
}
3282+
let last_segment = delegation.path.segments.last().unwrap();
3283+
let traits = self.traits_in_scope(last_segment.ident, ValueNS);
3284+
// Saving traits for a `MethodCall` that has not yet been generated.
3285+
self.r.delegation_trait_map.insert(delegation.id, traits);
3286+
32823287
self.visit_path(&delegation.path, delegation.id);
32833288
if let Some(body) = &delegation.body {
32843289
// `PatBoundCtx` is not necessary in this context
32853290
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
32863291

3287-
let span = delegation.path.segments.last().unwrap().ident.span;
32883292
self.fresh_binding(
3289-
Ident::new(kw::SelfLower, span),
3293+
Ident::new(kw::SelfLower, last_segment.ident.span),
32903294
delegation.id,
32913295
PatternSource::FnParam,
32923296
&mut bindings,

0 commit comments

Comments
 (0)