Skip to content

Commit 1e5a473

Browse files
committed
Delegation: support coercion for target expression
1 parent 894f7a4 commit 1e5a473

File tree

16 files changed

+313
-102
lines changed

16 files changed

+313
-102
lines changed

compiler/rustc_ast_lowering/src/delegation.rs

+117-74
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,17 +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-
// The value may be missing due to recursive delegation.
71-
// Error will be emmited later during HIR ty lowering.
72-
self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self)
73-
} else {
74-
match self.tcx.def_kind(sig_id) {
75-
DefKind::Fn => false,
76-
DefKind::AssocFn => self.tcx.associated_item(sig_id).fn_has_self_parameter,
77-
_ => span_bug!(span, "unexpected DefKind for delegation item"),
78-
}
79-
}
69+
self.has_self(sig_id, span)
8070
}
8171

8272
pub(crate) fn lower_delegation(
@@ -107,12 +97,29 @@ impl<'hir> LoweringContext<'_, 'hir> {
10797
span: Span,
10898
) -> Result<DefId, ErrorGuaranteed> {
10999
let sig_id = if self.is_in_trait_impl { item_id } else { path_id };
110-
let sig_id =
111-
self.resolver.get_partial_res(sig_id).and_then(|r| r.expect_full_res().opt_def_id());
112-
sig_id.ok_or_else(|| {
113-
self.tcx
114-
.dcx()
115-
.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+
)
116123
})
117124
}
118125

@@ -122,7 +129,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
122129
predicates: &[],
123130
has_where_clause_predicates: false,
124131
where_clause_span: span,
125-
span: span,
132+
span,
126133
})
127134
}
128135

@@ -222,12 +229,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
222229
}));
223230

224231
let path = self.arena.alloc(hir::Path { span, res: Res::Local(param_id), segments });
225-
226-
hir::Expr {
227-
hir_id: self.next_id(),
228-
kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
229-
span,
230-
}
232+
self.mk_expr(hir::ExprKind::Path(hir::QPath::Resolved(None, path)), span)
231233
}
232234

233235
fn lower_delegation_body(
@@ -236,25 +238,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
236238
param_count: usize,
237239
span: Span,
238240
) -> BodyId {
239-
let path = self.lower_qpath(
240-
delegation.id,
241-
&delegation.qself,
242-
&delegation.path,
243-
ParamMode::Optional,
244-
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
245-
None,
246-
);
247241
let block = delegation.body.as_deref();
248242

249243
self.lower_body(|this| {
250-
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
251-
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;
252247

253248
for idx in 0..param_count {
254249
let (param, pat_node_id) = this.generate_param(span);
255250
parameters.push(param);
256251

257-
let arg = if let Some(block) = block
252+
if let Some(block) = block
258253
&& idx == 0
259254
{
260255
let mut self_resolver = SelfResolver {
@@ -263,56 +258,103 @@ impl<'hir> LoweringContext<'_, 'hir> {
263258
self_param_id: pat_node_id,
264259
};
265260
self_resolver.visit_block(block);
266-
let block = this.lower_block(block, false);
267-
hir::Expr {
268-
hir_id: this.next_id(),
269-
kind: hir::ExprKind::Block(block, None),
270-
span: block.span,
271-
}
261+
target_expr = Some(this.lower_block_noalloc(block, false));
272262
} else {
273263
let pat_hir_id = this.lower_node_id(pat_node_id);
274-
this.generate_arg(pat_hir_id, span)
264+
let arg = this.generate_arg(pat_hir_id, span);
265+
args.push(arg);
275266
};
276-
args.push(arg);
277267
}
278268

279-
let args = self.arena.alloc_from_iter(args);
280-
let final_expr = this.generate_call(path, args);
269+
let final_expr = this.finalize_body_lowering(delegation, target_expr, args, span);
281270
(this.arena.alloc_from_iter(parameters), final_expr)
282271
})
283272
}
284273

285-
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(
286278
&mut self,
287-
path: hir::QPath<'hir>,
288-
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,
289283
) -> hir::Expr<'hir> {
290-
let callee = self.arena.alloc(hir::Expr {
291-
hir_id: self.next_id(),
292-
kind: hir::ExprKind::Path(path),
293-
span: path.span(),
294-
});
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+
};
295312

296-
let expr = self.arena.alloc(hir::Expr {
297-
hir_id: self.next_id(),
298-
kind: hir::ExprKind::Call(callee, args),
299-
span: path.span(),
300-
});
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+
}
301341

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+
};
302349
let block = self.arena.alloc(hir::Block {
303-
stmts: &[],
304-
expr: Some(expr),
305-
hir_id: self.next_id(),
350+
stmts,
351+
expr: Some(call),
352+
hir_id: block_id,
306353
rules: hir::BlockCheckMode::DefaultBlock,
307-
span: path.span(),
354+
span,
308355
targeted_by_break: false,
309356
});
310-
311-
hir::Expr {
312-
hir_id: self.next_id(),
313-
kind: hir::ExprKind::Block(block, None),
314-
span: path.span(),
315-
}
357+
self.mk_expr(hir::ExprKind::Block(block, None), span)
316358
}
317359

318360
fn generate_delegation_error(
@@ -333,11 +375,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
333375
let header = self.generate_header_error();
334376
let sig = hir::FnSig { decl, header, span };
335377

336-
let body_id = self.lower_body(|this| {
337-
let expr =
338-
hir::Expr { hir_id: this.next_id(), kind: hir::ExprKind::Err(err), span: span };
339-
(&[], expr)
340-
});
378+
let body_id = self.lower_body(|this| (&[], this.mk_expr(hir::ExprKind::Err(err), span)));
341379
DelegationResults { generics, body_id, sig }
342380
}
343381

@@ -349,6 +387,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
349387
abi: abi::Abi::Rust,
350388
}
351389
}
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+
}
352395
}
353396

354397
struct SelfResolver<'a> {

compiler/rustc_hir_typeck/src/method/probe.rs

+46
Original file line numberDiff line numberDiff line change
@@ -1218,8 +1218,54 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
12181218
)>,
12191219
mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
12201220
) -> Option<PickResult<'tcx>> {
1221+
// Delegation item can be expanded into method calls or fully qualified calls
1222+
// depending on the callee's signature. Method calls are used to allow
1223+
// autoref/autoderef for target expression. For example in:
1224+
//
1225+
// trait Trait : Sized {
1226+
// fn by_value(self) -> i32 { 1 }
1227+
// fn by_mut_ref(&mut self) -> i32 { 2 }
1228+
// fn by_ref(&self) -> i32 { 3 }
1229+
// }
1230+
//
1231+
// struct NewType(SomeType);
1232+
// impl Trait for NewType {
1233+
// reuse Trait::* { self.0 }
1234+
// }
1235+
//
1236+
// `self.0` will automatically coerce. The difference with existing method lookup
1237+
// is that methods in delegation items are pre-resolved by callee path (`Trait::*`).
1238+
// Therefore, we are only forced to consider the pre-resolved method here.
1239+
let delegation_res = if self.fcx.tcx.hir().opt_delegation_sig_id(self.body_id).is_some() {
1240+
let body = self.tcx.hir().body_owned_by(self.body_id);
1241+
let block = match body.value.kind {
1242+
hir::ExprKind::Block(block, _) => block,
1243+
_ => unreachable!(),
1244+
};
1245+
let expr = block.expr.unwrap();
1246+
let res_id = match expr.kind {
1247+
hir::ExprKind::MethodCall(segment, ..) => segment.res.def_id(),
1248+
hir::ExprKind::Call(
1249+
hir::Expr { kind: hir::ExprKind::Path(hir::QPath::Resolved(_, path)), .. },
1250+
_,
1251+
) => path.res.def_id(),
1252+
_ => unreachable!(),
1253+
};
1254+
Some((expr.hir_id, res_id))
1255+
} else {
1256+
None
1257+
};
12211258
let mut applicable_candidates: Vec<_> = candidates
12221259
.iter()
1260+
.filter(|candidate| {
1261+
if let Some((expr_id, res_id)) = delegation_res
1262+
&& self.scope_expr_id == expr_id
1263+
&& candidate.item.def_id != res_id
1264+
{
1265+
return false;
1266+
}
1267+
true
1268+
})
12231269
.map(|probe| {
12241270
(probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates))
12251271
})

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
@@ -224,8 +224,10 @@ pub struct ResolverAstLowering {
224224
/// Lints that were emitted by the resolver and early lints.
225225
pub lint_buffer: Steal<LintBuffer>,
226226

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

231233
#[derive(Debug)]

compiler/rustc_resolve/src/late.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -3315,15 +3315,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
33153315
if let Some(qself) = &delegation.qself {
33163316
self.visit_ty(&qself.ty);
33173317
}
3318+
let last_segment = delegation.path.segments.last().unwrap();
3319+
let traits = self.traits_in_scope(last_segment.ident, ValueNS);
3320+
// Saving traits for a `MethodCall` that has not yet been generated.
3321+
self.r.delegation_trait_map.insert(delegation.id, traits);
3322+
33183323
self.visit_path(&delegation.path, delegation.id);
33193324
if let Some(body) = &delegation.body {
33203325
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
33213326
// `PatBoundCtx` is not necessary in this context
33223327
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
33233328

3324-
let span = delegation.path.segments.last().unwrap().ident.span;
33253329
this.fresh_binding(
3326-
Ident::new(kw::SelfLower, span),
3330+
Ident::new(kw::SelfLower, last_segment.ident.span),
33273331
delegation.id,
33283332
PatternSource::FnParam,
33293333
&mut bindings,

0 commit comments

Comments
 (0)