rustc_ast_lowering/delegation/
resolution.rs1use std::ops::ControlFlow;
2
3use ast::visit::Visitor;
4use hir::def::DefKind;
5use rustc_ast as ast;
6use rustc_ast::*;
7use rustc_data_structures::fx::FxHashSet;
8use rustc_hir as hir;
9use rustc_middle::span_bug;
10use rustc_span::def_id::{DefId, LocalDefId};
11use rustc_span::{ErrorGuaranteed, Span};
12
13use crate::delegation::generics::GenericsGenerationResults;
14use crate::delegation::resolution::resolver::DelegationResolver;
15use crate::diagnostics::{
16 CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion,
17 DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee,
18};
19
20#[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)]
22pub(super) struct ParamInfo {
23 pub param_count: usize,
25
26 pub c_variadic: bool,
28
29 pub splatted: Option<u8>,
31}
32
33pub(super) struct DelegationResolution {
34 pub sig_id: DefId,
35 pub is_method: bool,
36 pub param_info: ParamInfo,
37 pub span: Span,
38 pub should_generate_block: bool,
39 pub call_path_res: Option<DefId>,
40 pub source: DelegationSource,
41 pub output_self_mapping: Option<(LocalDefId, bool)>,
42}
43
44pub(super) mod resolver {
45 use rustc_ast::NodeId;
46 use rustc_hir::def_id::{DefId, LocalDefId};
47 use rustc_middle::ty::TyCtxt;
48
49 use crate::LoweringContext;
50
51 pub(crate) struct DelegationResolver<'a, 'hir>(&'a LoweringContext<'a, 'hir>);
55
56 impl<'a, 'tcx> DelegationResolver<'a, 'tcx> {
57 pub(crate) fn new(ctx: &'a LoweringContext<'a, 'tcx>) -> Self {
58 DelegationResolver(ctx)
59 }
60
61 #[inline]
62 pub(crate) fn tcx(&self) -> TyCtxt<'tcx> {
63 self.0.tcx
64 }
65
66 #[inline]
67 pub(crate) fn owner_id(&self) -> LocalDefId {
68 self.0.owner.def_id
69 }
70
71 #[inline]
84 pub(crate) fn is_definition(&self, id: NodeId) -> bool {
85 self.0.resolver.owners.contains_key(&id)
86 || self.0.owner.node_id_to_def_id.contains_key(&id)
87 }
88
89 #[inline]
90 pub(crate) fn get_resolution_id(&self, id: NodeId) -> Option<DefId> {
91 self.0.get_partial_res(id).and_then(|r| r.expect_full_res().opt_def_id())
92 }
93 }
94}
95
96impl<'tcx> DelegationResolver<'_, 'tcx> {
97 pub(super) fn resolve_delegation(
98 &self,
99 delegation: &Delegation,
100 span: Span,
101 ) -> Result<(DelegationResolution, GenericsGenerationResults<'tcx>), ErrorGuaranteed> {
102 let tcx = self.tcx();
103 let def_id = self.owner_id();
104
105 let sig_id = tcx
108 .resolutions(())
109 .delegation_infos
110 .get(&def_id)
111 .map(|info| {
112 info.resolution_id.and_then(|id| self.check_for_cycles(id, span).map(|_| id))
113 })
114 .unwrap_or_else(|| {
115 Err(tcx.dcx().span_delayed_bug(
116 span,
117 ::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),
118 ))
119 })?;
120
121 let is_method = match tcx.def_kind(sig_id) {
122 DefKind::Fn => false,
123 DefKind::AssocFn => tcx.associated_item(sig_id).is_method(),
124 _ => ::rustc_middle::util::bug::span_bug_fmt(span,
format_args!("unexpected DefKind for delegation item"))span_bug!(span, "unexpected DefKind for delegation item"),
125 };
126
127 let sig = tcx.fn_sig(sig_id).skip_binder().skip_binder();
128 let param_count = sig.inputs().len() + usize::from(sig.c_variadic());
129
130 let res = DelegationResolution {
131 is_method,
132 span,
133 sig_id,
134 param_info: ParamInfo { param_count, c_variadic: sig.c_variadic(), splatted: None },
136 should_generate_block: self.check_block_soundness(
137 delegation,
138 sig_id,
139 is_method,
140 param_count,
141 )?,
142 source: delegation.source,
143 call_path_res: self.get_resolution_id(delegation.id),
144 output_self_mapping: self.should_map_return_value(delegation),
145 };
146
147 Ok((res, self.resolve_and_generate_generics(delegation, sig_id)))
148 }
149
150 fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> {
151 let tcx = self.tcx();
152 let mut visited: FxHashSet<DefId> = Default::default();
153
154 loop {
155 visited.insert(def_id);
156
157 if let Some(local_id) = def_id.as_local()
161 && let Some(info) = tcx.resolutions(()).delegation_infos.get(&local_id)
162 && let Ok(id) = info.resolution_id
163 {
164 def_id = id;
165 if visited.contains(&def_id) {
166 return Err(match visited.len() {
167 1 => tcx.dcx().emit_err(UnresolvedDelegationCallee { span }),
168 _ => tcx.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
169 });
170 }
171 } else {
172 return Ok(());
173 }
174 }
175 }
176
177 fn check_block_soundness(
178 &self,
179 delegation: &Delegation,
180 sig_id: DefId,
181 is_method: bool,
182 param_count: usize,
183 ) -> Result<bool, ErrorGuaranteed> {
184 let tcx = self.tcx();
185 let should_generate_block = is_method
186 || #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(sig_id) {
DefKind::Fn => true,
_ => false,
}matches!(tcx.def_kind(sig_id), DefKind::Fn)
187 || #[allow(non_exhaustive_omitted_patterns)] match delegation.source {
DelegationSource::Single => true,
_ => false,
}matches!(delegation.source, DelegationSource::Single);
188
189 let Some(block) = &delegation.body else { return Ok(should_generate_block) };
190
191 if param_count == 0 && should_generate_block {
194 let err = DelegationBlockSpecifiedWhenNoParams { span: block.span };
195 return Err(tcx.dcx().emit_err(err));
196 }
197
198 struct DefinitionsFinder<'a, 'hir> {
199 ctx: &'a DelegationResolver<'a, 'hir>,
200 }
201
202 impl<'a> Visitor<'a> for DefinitionsFinder<'a, '_> {
203 type Result = ControlFlow<()>;
204
205 fn visit_id(&mut self, id: NodeId) -> Self::Result {
206 match self.ctx.is_definition(id) {
207 true => ControlFlow::Break(()),
208 false => ControlFlow::Continue(()),
209 }
210 }
211 }
212
213 let mut collector = DefinitionsFinder { ctx: self };
214
215 let contains_defs = collector.visit_block(block).is_break();
216
217 if should_generate_block || !contains_defs {
220 Ok(should_generate_block)
221 } else {
222 Err(tcx.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }))
223 }
224 }
225
226 fn should_map_return_value(&self, delegation: &Delegation) -> Option<(LocalDefId, bool)> {
227 if delegation.body.is_none() {
229 return None;
230 }
231
232 let tcx = self.tcx();
233 let parent = tcx.local_parent(self.owner_id());
234 let parent_kind = tcx.def_kind(parent);
235
236 if !#[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(parent) {
DefKind::Impl { .. } => true,
_ => false,
}matches!(tcx.def_kind(parent), DefKind::Impl { .. }) {
247 return None;
248 }
249
250 let is_trait_impl = parent_kind == DefKind::Impl { of_trait: true };
251
252 Some((parent, is_trait_impl)).filter(|_| {
254 self.get_resolution_id(delegation.id).is_some_and(|id| {
255 tcx.def_kind(id) == DefKind::AssocFn
256 && tcx.def_kind(tcx.parent(id)) == DefKind::Trait
260 && tcx.fn_sig(id).skip_binder().output().skip_binder().is_param(0)
261 })
262 })
263 }
264}