rustc_ast_lowering/delegation/
resolution.rs1use std::ops::ControlFlow;
2
3use ast::visit::Visitor;
4use hir::def::DefKind;
5use rustc_ast::{self as ast, Delegation, DelegationSource, NodeId};
6use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
7use rustc_hir as hir;
8use rustc_middle::ty::Ty;
9use rustc_middle::{span_bug, ty};
10use rustc_span::def_id::{DefId, LocalDefId};
11use rustc_span::{ErrorGuaranteed, Span, kw};
12
13use crate::delegation::generics::GenericsGenerationResults;
14use crate::delegation::resolution::resolver::DelegationResolver;
15use crate::diagnostics::{
16 CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion,
17 DelegationAttemptedBlockWithDefsRelowering, DelegationBlockSpecifiedWhenNoParams,
18 UnresolvedDelegationCallee,
19};
20
21#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ParamInfo {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "ParamInfo",
"param_count", &self.param_count, "c_variadic", &self.c_variadic,
"splatted", &&self.splatted)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for ParamInfo {
#[inline]
fn clone(&self) -> ParamInfo {
let _: ::core::clone::AssertParamIsClone<usize>;
let _: ::core::clone::AssertParamIsClone<bool>;
let _: ::core::clone::AssertParamIsClone<Option<u8>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ParamInfo { }Copy, #[automatically_derived]
impl ::core::cmp::Eq for ParamInfo {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<usize>;
let _: ::core::cmp::AssertParamIsEq<bool>;
let _: ::core::cmp::AssertParamIsEq<Option<u8>>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialEq for ParamInfo {
#[inline]
fn eq(&self, other: &ParamInfo) -> bool {
self.c_variadic == other.c_variadic &&
self.param_count == other.param_count &&
self.splatted == other.splatted
}
}PartialEq)]
23pub(super) struct ParamInfo {
24 pub param_count: usize,
26
27 pub c_variadic: bool,
29
30 pub splatted: Option<u8>,
32}
33
34#[derive(#[automatically_derived]
impl ::core::default::Default for SigMapping {
#[inline]
fn default() -> SigMapping {
SigMapping {
map_return: ::core::default::Default::default(),
arguments_to_map: ::core::default::Default::default(),
}
}
}Default)]
35pub(super) struct SigMapping {
36 pub map_return: bool,
37 pub arguments_to_map: FxIndexSet<usize>,
38}
39
40pub(super) struct DelegationResolution {
41 pub sig_id: DefId,
42 pub is_method: bool,
43 pub param_info: ParamInfo,
44 pub span: Span,
45 pub call_path_res: DefId,
46 pub source: DelegationSource,
47 pub parent: LocalDefId,
48 pub sig_mapping: SigMapping,
49}
50
51pub(super) mod resolver {
52 use rustc_ast::NodeId;
53 use rustc_hir::def_id::{DefId, LocalDefId};
54 use rustc_middle::ty::TyCtxt;
55 use rustc_span::ErrorGuaranteed;
56
57 use crate::LoweringContext;
58
59 pub(crate) struct DelegationResolver<'a, 'hir>(&'a LoweringContext<'a, 'hir>);
63
64 impl<'a, 'tcx> DelegationResolver<'a, 'tcx> {
65 pub(crate) fn new(ctx: &'a LoweringContext<'a, 'tcx>) -> Self {
66 DelegationResolver(ctx)
67 }
68
69 #[inline]
70 pub(crate) fn tcx(&self) -> TyCtxt<'tcx> {
71 self.0.tcx
72 }
73
74 #[inline]
75 pub(crate) fn owner_id(&self) -> LocalDefId {
76 self.0.owner.def_id
77 }
78
79 #[inline]
92 pub(crate) fn is_definition(&self, id: NodeId) -> bool {
93 self.0.resolver.owners.contains_key(&id)
94 || self.0.owner.node_id_to_def_id.contains_key(&id)
95 }
96
97 #[inline]
98 pub(crate) fn get_resolution_id(&self, id: NodeId) -> Result<DefId, ErrorGuaranteed> {
99 self.0.get_partial_res(id).and_then(|r| r.expect_full_res().opt_def_id()).ok_or_else(
100 || self.tcx().dcx().delayed_bug(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("failed to resolve node {0:?}", id))
})format!("failed to resolve node {id:?}")),
101 )
102 }
103 }
104}
105
106impl<'tcx> DelegationResolver<'_, 'tcx> {
107 pub(super) fn resolve_delegation(
108 &self,
109 delegation: &Delegation,
110 span: Span,
111 ) -> Result<(DelegationResolution, GenericsGenerationResults<'tcx>), ErrorGuaranteed> {
112 let tcx = self.tcx();
113 let def_id = self.owner_id();
114
115 let sig_id = tcx
118 .resolutions(())
119 .delegation_infos
120 .get(&def_id)
121 .map(|info| {
122 info.resolution_id.and_then(|id| self.check_for_cycles(id, span).map(|_| id))
123 })
124 .unwrap_or_else(|| {
125 Err(tcx.dcx().span_delayed_bug(
126 span,
127 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("delegation resolution record was not found for {0:?}",
def_id))
})format!("delegation resolution record was not found for {:?}", def_id),
128 ))
129 })?;
130
131 let is_method = match tcx.def_kind(sig_id) {
132 DefKind::Fn => false,
133 DefKind::AssocFn => tcx.associated_item(sig_id).is_method(),
134 _ => ::rustc_middle::util::bug::span_bug_fmt(span,
format_args!("unexpected DefKind for delegation item"))span_bug!(span, "unexpected DefKind for delegation item"),
135 };
136
137 let sig = tcx.fn_sig(sig_id).skip_binder().skip_binder();
138 let param_count = sig.inputs().len() + usize::from(sig.c_variadic());
139 let parent = tcx.local_parent(def_id);
140
141 let (should_generate_block, contains_defs) =
142 self.check_block_soundness(delegation, sig_id, is_method, param_count)?;
143
144 let res = DelegationResolution {
145 is_method,
146 span,
147 sig_id,
148 parent,
149 param_info: ParamInfo { param_count, c_variadic: sig.c_variadic(), splatted: None },
151 source: delegation.source,
152 call_path_res: self.get_resolution_id(delegation.id)?,
153 sig_mapping: self.create_sig_mapping(
154 delegation,
155 span,
156 should_generate_block,
157 parent,
158 sig,
159 contains_defs,
160 )?,
161 };
162
163 Ok((res, self.resolve_and_generate_generics(delegation, sig_id)?))
164 }
165
166 fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> {
167 let tcx = self.tcx();
168 let mut visited: FxHashSet<DefId> = Default::default();
169
170 loop {
171 visited.insert(def_id);
172
173 if let Some(local_id) = def_id.as_local()
177 && let Some(info) = tcx.resolutions(()).delegation_infos.get(&local_id)
178 && let Ok(id) = info.resolution_id
179 {
180 def_id = id;
181 if visited.contains(&def_id) {
182 return Err(match visited.len() {
183 1 => tcx.dcx().emit_err(UnresolvedDelegationCallee { span }),
184 _ => tcx.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
185 });
186 }
187 } else {
188 return Ok(());
189 }
190 }
191 }
192
193 fn check_block_soundness(
194 &self,
195 delegation: &Delegation,
196 sig_id: DefId,
197 is_method: bool,
198 param_count: usize,
199 ) -> Result<(bool, bool), ErrorGuaranteed> {
200 let tcx = self.tcx();
201 let should_generate_block = is_method
202 || #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(sig_id) {
DefKind::Fn => true,
_ => false,
}matches!(tcx.def_kind(sig_id), DefKind::Fn)
203 || #[allow(non_exhaustive_omitted_patterns)] match delegation.source {
DelegationSource::Single => true,
_ => false,
}matches!(delegation.source, DelegationSource::Single);
204
205 let Some(block) = &delegation.body else { return Ok((should_generate_block, false)) };
206
207 if param_count == 0 && should_generate_block {
210 let err = DelegationBlockSpecifiedWhenNoParams { span: block.span };
211 return Err(tcx.dcx().emit_err(err));
212 }
213
214 struct DefinitionsFinder<'a, 'hir> {
215 resolver: &'a DelegationResolver<'a, 'hir>,
216 }
217
218 impl<'a> Visitor<'a> for DefinitionsFinder<'a, '_> {
219 type Result = ControlFlow<()>;
220
221 fn visit_id(&mut self, id: NodeId) -> Self::Result {
222 match self.resolver.is_definition(id) {
223 true => ControlFlow::Break(()),
224 false => ControlFlow::Continue(()),
225 }
226 }
227 }
228
229 let mut collector = DefinitionsFinder { resolver: self };
230
231 let contains_defs = collector.visit_block(block).is_break();
232
233 if should_generate_block || !contains_defs {
236 Ok((should_generate_block, contains_defs))
237 } else {
238 Err(tcx.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }))
239 }
240 }
241
242 fn create_sig_mapping(
243 &self,
244 delegation: &Delegation,
245 span: Span,
246 should_generate_block: bool,
247 parent: LocalDefId,
248 sig: ty::FnSig<'tcx>,
249 contains_defs: bool,
250 ) -> Result<SigMapping, ErrorGuaranteed> {
251 let mut mapping = SigMapping::default();
252 if should_generate_block {
253 mapping.arguments_to_map.insert(0);
254 }
255
256 if self.can_perform_self_mapping(delegation, parent)? {
257 mapping.map_return = sig.output().is_param(0);
260
261 let self_param = Ty::new_param(self.tcx(), 0, kw::SelfUpper);
262 let arguments_to_map = sig
263 .inputs()
264 .iter()
265 .enumerate()
266 .skip(1) .filter_map(|(idx, param)| param.contains(self_param).then_some(idx));
268
269 mapping.arguments_to_map.extend(arguments_to_map);
270 }
271
272 if contains_defs && mapping.arguments_to_map.len() > 1 {
275 return Err(self
276 .tcx()
277 .dcx()
278 .emit_err(DelegationAttemptedBlockWithDefsRelowering { span }));
279 }
280
281 Ok(mapping)
282 }
283
284 fn can_perform_self_mapping(
285 &self,
286 delegation: &Delegation,
287 parent: LocalDefId,
288 ) -> Result<bool, ErrorGuaranteed> {
289 if delegation.body.is_none() {
291 return Ok(false);
292 }
293
294 let tcx = self.tcx();
295
296 if !#[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(parent) {
DefKind::Impl { .. } => true,
_ => false,
}matches!(tcx.def_kind(parent), DefKind::Impl { .. }) {
307 return Ok(false);
308 }
309
310 let id = self.get_resolution_id(delegation.id)?;
314 Ok(tcx.def_kind(id) == DefKind::AssocFn && tcx.def_kind(tcx.parent(id)) == DefKind::Trait)
315 }
316}