Skip to main content

rustc_ast_lowering/delegation/
resolution.rs

1use std::ops::ControlFlow;
2
3use ast::visit::Visitor;
4use hir::def::DefKind;
5use rustc_ast::{self as ast, AssocItemKind, Delegation, DelegationSource, Item, ItemKind, NodeId};
6use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
7use rustc_data_structures::steal::Steal;
8use rustc_hir as hir;
9use rustc_middle::middle::resolve::{
10    self as mid_res, AstOwner, DelegationInherentFnKind, TypeRelativeDelegationRes,
11};
12use rustc_middle::ty::{
13    self as ty, AssocKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
14};
15use rustc_span::def_id::{DefId, LocalDefId};
16use rustc_span::{ErrorGuaranteed, Span};
17
18use crate::delegation::generics::GenericsGenerationResults;
19use crate::delegation::resolution::resolver::DelegationResolver;
20use crate::diagnostics::{
21    AmbiguousDelegationToInherentImpl, CycleInDelegationSignatureResolution,
22    DelegationAttemptedBlockWithDefsDeletion, DelegationAttemptedBlockWithDefsRelowering,
23    DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee,
24};
25
26/// Simple (hack or heuristic) resolution of some delegations to inherent impls
27/// while correct resolution through `ProbeContext` is not available
28/// during AST -> HIR lowering due to query cycles.
29/// Successful resolutions from this heuristics are not a subset of
30/// successful resolutions from the correct approach, if we want to stabilize
31/// delegations to inherent impls with this approach we need a second pass in type checking
32/// (i.e., when there's no cycles) that makes sure that resolutions from
33/// the heuristic match the correct resolutions, or report errors otherwise.
34/// FIXME(fn_delegation): correct resolution through `ProbeContext` engine
35pub(crate) fn resolve_type_relative_delegations(
36    tcx: TyCtxt<'_>,
37    _: (),
38) -> FxIndexMap<LocalDefId, TypeRelativeDelegationRes> {
39    let ast_index = tcx.index_ast(());
40    let resolutions = tcx.resolutions(());
41
42    let infos = &resolutions.delegation_infos;
43    let inh_fns = &resolutions.delegation_inherent_fn_map;
44
45    let mut type_relative_resolutions: FxIndexMap<LocalDefId, TypeRelativeDelegationRes> =
46        Default::default();
47
48    for (&def_id, res) in infos {
49        match res.resolution {
50            mid_res::DelegationResolution::Error(..) | mid_res::DelegationResolution::Full(_) => {
51                continue;
52            }
53            // Also record resolutions for cases when signature is resolved but call path is not.
54            mid_res::DelegationResolution::Partial
55            | mid_res::DelegationResolution::PartialCall(_) => {
56                let Some(r_and_owner) = ast_index.get(def_id).map(Steal::borrow) else {
57                    {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("ast index must contain delegations")));
};unreachable!("ast index must contain delegations");
58                };
59
60                let (r, owner) = &*r_and_owner;
61
62                let delegation = match owner {
63                    AstOwner::Item(Item { kind: ItemKind::Delegation(d), .. })
64                    | AstOwner::TraitItem(Item { kind: AssocItemKind::Delegation(d), .. })
65                    | AstOwner::ImplItem(Item { kind: AssocItemKind::Delegation(d), .. }) => d,
66                    _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("we are processing only delegations")));
}unreachable!("we are processing only delegations"),
67                };
68
69                let res = r.partial_res_map.get(&delegation.id);
70                let res = res.and_then(|res| res.base_res().opt_def_id());
71                let ident = delegation.path.segments.last().map(|s| s.ident);
72
73                let span = delegation.last_segment_span();
74
75                let ambig_error_res = || {
76                    TypeRelativeDelegationRes::Ambig(
77                        tcx.dcx().span_delayed_bug(span, "ambiguous delegation to inherent impl"),
78                    )
79                };
80
81                let default_error_res =
82                    || {
83                        TypeRelativeDelegationRes::Error(tcx.dcx().span_delayed_bug(
84                            span,
85                            "failed to resolve delegation to inherent impl",
86                        ))
87                    };
88
89                let res = if let Some(res) = res
90                    && let Some(ident) = ident
91                {
92                    match res.as_local() {
93                        Some(local_def_id) => {
94                            let res = inh_fns.get(&local_def_id).and_then(|map| map.get(&ident));
95
96                            match res {
97                                Some(res) => match res {
98                                    DelegationInherentFnKind::Ambig => ambig_error_res(),
99                                    DelegationInherentFnKind::Single(res) => {
100                                        TypeRelativeDelegationRes::Ok(res.to_def_id())
101                                    }
102                                },
103                                _ => default_error_res(),
104                            }
105                        }
106                        None => {
107                            let mut sig_res = None;
108                            'inh_loop: for inh_impl_id in tcx.inherent_impls(res) {
109                                let assoc_items = tcx.associated_items(*inh_impl_id);
110
111                                // FIXME(fn_delegation): use correct identifier hygiene
112                                let mut candidates = assoc_items
113                                    .filter_by_name_unhygienic(ident.name)
114                                    .filter(|it| #[allow(non_exhaustive_omitted_patterns)] match it.kind {
    AssocKind::Fn { .. } => true,
    _ => false,
}matches!(it.kind, AssocKind::Fn { .. }));
115
116                                while let Some(candidate) = candidates.next() {
117                                    if sig_res.is_some() {
118                                        sig_res = Some(ambig_error_res());
119                                        break 'inh_loop;
120                                    } else {
121                                        sig_res =
122                                            Some(TypeRelativeDelegationRes::Ok(candidate.def_id));
123                                    }
124                                }
125                            }
126
127                            sig_res.unwrap_or_else(default_error_res)
128                        }
129                    }
130                } else {
131                    default_error_res()
132                };
133
134                type_relative_resolutions.insert(def_id, res);
135            }
136        }
137    }
138
139    type_relative_resolutions
140}
141
142/// Summary info about function parameters.
143#[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]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ParamInfo { }
#[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::marker::StructuralPartialEq for ParamInfo { }
#[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)]
144pub(super) struct ParamInfo {
145    /// The number of function parameters, including any C variadic `...` parameter.
146    pub param_count: usize,
147
148    /// Whether the function arguments end in a C variadic `...` parameter.
149    pub c_variadic: bool,
150
151    /// The index of the splatted parameter, if any.
152    pub splatted: Option<u8>,
153}
154
155#[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, #[automatically_derived]
impl ::core::fmt::Debug for SigMapping {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "SigMapping",
            "map_return", &self.map_return, "arguments_to_map",
            &&self.arguments_to_map)
    }
}Debug)]
156pub(super) struct SigMapping {
157    pub map_return: bool,
158    pub arguments_to_map: FxIndexSet<usize>,
159}
160
161pub(super) struct DelegationResolution {
162    pub sig_id: DefId,
163    pub is_method: bool,
164    pub param_info: ParamInfo,
165    pub span: Span,
166    pub call_path_res: DefId,
167    pub source: DelegationSource,
168    pub parent: LocalDefId,
169    pub sig_mapping: SigMapping,
170}
171
172pub(super) mod resolver {
173    use rustc_ast::NodeId;
174    use rustc_hir::def_id::{DefId, LocalDefId};
175    use rustc_middle::ty::TyCtxt;
176    use rustc_span::ErrorGuaranteed;
177
178    use crate::LoweringContext;
179
180    /// Abstracts operations that are needed for delegation's resolution, so resolution
181    /// is independent of `LoweringContext`. Placed in a separate module so `LoweringContext`
182    /// can not be accessed directly.
183    pub(crate) struct DelegationResolver<'a, 'hir>(&'a LoweringContext<'a, 'hir>);
184
185    impl<'a, 'tcx> DelegationResolver<'a, 'tcx> {
186        pub(crate) fn new(ctx: &'a LoweringContext<'a, 'tcx>) -> Self {
187            DelegationResolver(ctx)
188        }
189
190        #[inline]
191        pub(crate) fn tcx(&self) -> TyCtxt<'tcx> {
192            self.0.tcx
193        }
194
195        #[inline]
196        pub(crate) fn owner_id(&self) -> LocalDefId {
197            self.0.curr_owner.owner.def_id
198        }
199
200        /// (from `tests\ui\delegation\target-expr-removal-defs-inside.rs`):
201        /// ```rust
202        /// reuse impl Trait for S1 {
203        ///     some::path::<{ fn foo() {} }>::xd();
204        ///     fn foo() {}
205        ///     self.0
206        /// }
207        /// ```
208        ///
209        /// Constant from unresolved path will be in `node_id_to_def_id`,
210        /// `fn foo() {}` will not be in `node_id_to_def_id` but will be in `owners`,
211        /// both have `LocalDefId`, so we check those two maps.
212        #[inline]
213        pub(crate) fn is_definition(&self, id: NodeId) -> bool {
214            self.0.resolver.owners.contains_key(&id)
215                || self.0.curr_owner.owner.node_id_to_def_id.contains_key(&id)
216        }
217
218        #[inline]
219        pub(crate) fn get_resolution_id(&self, id: NodeId) -> Result<DefId, ErrorGuaranteed> {
220            self.0.get_partial_res(id).and_then(|r| r.expect_full_res().opt_def_id()).ok_or_else(
221                || 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:?}")),
222            )
223        }
224    }
225}
226
227impl<'tcx> DelegationResolver<'_, 'tcx> {
228    pub(super) fn resolve_delegation(
229        &self,
230        delegation: &Delegation,
231        span: Span,
232    ) -> Result<(DelegationResolution, GenericsGenerationResults<'tcx>), ErrorGuaranteed> {
233        let tcx = self.tcx();
234        let def_id = self.owner_id();
235
236        // Delegation can be missing from the `delegations_resolutions` table
237        // in illegal places such as function bodies in extern blocks (see #151356).
238        let sig_id = self.resolve_delegation_sig(def_id, span)?;
239
240        let create_invalid_path_error =
241            || tcx.dcx().span_delayed_bug(span, "invalid delegation path");
242
243        match &delegation.path.segments[..] {
244            [] => return Err(create_invalid_path_error()),
245            [child] => {
246                let res = self.get_resolution_id(child.id)?;
247                if tcx.def_kind(res) != DefKind::Fn {
248                    return Err(create_invalid_path_error());
249                }
250            }
251            [.., parent, _] => {
252                let child_res = self.get_call_path_res(delegation, span)?;
253                let parent_res = self.get_resolution_id(parent.id)?;
254
255                match (tcx.def_kind(child_res), tcx.def_kind(parent_res)) {
256                    (DefKind::Fn, DefKind::Mod) => {}
257                    (DefKind::AssocFn, DefKind::Trait | DefKind::Struct | DefKind::Enum) => {}
258                    _ => return Err(create_invalid_path_error()),
259                }
260            }
261        }
262
263        self.check_for_cycles(sig_id, span)?;
264
265        let is_method = tcx.is_method(sig_id);
266        let sig = tcx.fn_sig(sig_id).skip_binder().skip_binder();
267        let param_count = sig.inputs().len() + usize::from(sig.c_variadic());
268        let parent = tcx.local_parent(def_id);
269
270        let (should_generate_block, contains_defs) =
271            self.check_block_soundness(delegation, sig_id, is_method, param_count)?;
272
273        let res = DelegationResolution {
274            is_method,
275            span,
276            sig_id,
277            parent,
278            // FIXME(splat): use `sig.splatted()` once FnSig has it
279            param_info: ParamInfo { param_count, c_variadic: sig.c_variadic(), splatted: None },
280            source: delegation.source,
281            call_path_res: self.get_call_path_res(delegation, span)?,
282            sig_mapping: self.create_sig_mapping(
283                delegation,
284                span,
285                should_generate_block,
286                parent,
287                sig,
288                contains_defs,
289            )?,
290        };
291
292        Ok((res, self.resolve_and_generate_generics(delegation, sig_id, span)?))
293    }
294
295    fn get_call_path_res(
296        &self,
297        delegation: &Delegation,
298        span: Span,
299    ) -> Result<DefId, ErrorGuaranteed> {
300        let def_id = self.owner_id();
301
302        match self.tcx().resolutions(()).delegation_infos[&def_id].resolution {
303            mid_res::DelegationResolution::Full(_) => self.get_resolution_id(delegation.id),
304            mid_res::DelegationResolution::Partial
305            | mid_res::DelegationResolution::PartialCall(_) => {
306                self.resolve_type_relative_delegation_sig(def_id, span)
307            }
308            mid_res::DelegationResolution::Error(err) => Err(err),
309        }
310    }
311
312    fn resolve_delegation_sig(
313        &self,
314        def_id: LocalDefId,
315        span: Span,
316    ) -> Result<DefId, ErrorGuaranteed> {
317        let tcx = self.tcx();
318
319        match tcx.resolutions(()).delegation_infos.get(&def_id) {
320            Some(res) => match res.resolution {
321                mid_res::DelegationResolution::Error(err) => Err(err),
322                mid_res::DelegationResolution::Full(def_id)
323                | mid_res::DelegationResolution::PartialCall(def_id) => Ok(def_id),
324                mid_res::DelegationResolution::Partial => {
325                    self.resolve_type_relative_delegation_sig(def_id, span)
326                }
327            },
328            None => Err(self.create_unresolved_error(def_id, span)),
329        }
330    }
331
332    fn create_unresolved_error(&self, def_id: LocalDefId, span: Span) -> ErrorGuaranteed {
333        self.tcx().dcx().span_delayed_bug(span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("unresolved delegation {0:?}",
                def_id))
    })format!("unresolved delegation {def_id:?}"))
334    }
335
336    fn resolve_type_relative_delegation_sig(
337        &self,
338        def_id: LocalDefId,
339        span: Span,
340    ) -> Result<DefId, ErrorGuaranteed> {
341        let tcx = self.tcx();
342
343        match tcx.resolve_type_relative_delegations(()).get(&def_id) {
344            Some(res) => match *res {
345                TypeRelativeDelegationRes::Ok(sig_id) => Ok(sig_id),
346                TypeRelativeDelegationRes::Error(err) => Err(err),
347                TypeRelativeDelegationRes::Ambig(_) => {
348                    Err(tcx.dcx().emit_err(AmbiguousDelegationToInherentImpl { span }))
349                }
350            },
351            None => Err(self.create_unresolved_error(def_id, span)),
352        }
353    }
354
355    fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> {
356        let tcx = self.tcx();
357        let mut visited: FxHashSet<DefId> = Default::default();
358        let delegation_infos = &tcx.resolutions(()).delegation_infos;
359
360        loop {
361            visited.insert(def_id);
362
363            // If def_id is in local crate and it corresponds to another delegation
364            // it means that we refer to another delegation as a callee, so in order to obtain
365            // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it.
366            if let Some(local_id) = def_id.as_local()
367                && delegation_infos.contains_key(&local_id)
368                && let Ok(id) = self.resolve_delegation_sig(local_id, span)
369            {
370                def_id = id;
371                if visited.contains(&def_id) {
372                    return Err(match visited.len() {
373                        1 => tcx.dcx().emit_err(UnresolvedDelegationCallee { span }),
374                        _ => tcx.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
375                    });
376                }
377            } else {
378                return Ok(());
379            }
380        }
381    }
382
383    fn check_block_soundness(
384        &self,
385        delegation: &Delegation,
386        sig_id: DefId,
387        is_method: bool,
388        param_count: usize,
389    ) -> Result<(/* should generate block */ bool, /* contains defs */ bool), ErrorGuaranteed> {
390        let tcx = self.tcx();
391        let should_generate_block = is_method
392            || #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(sig_id) {
    DefKind::Fn => true,
    _ => false,
}matches!(tcx.def_kind(sig_id), DefKind::Fn)
393            || #[allow(non_exhaustive_omitted_patterns)] match delegation.source {
    DelegationSource::Single => true,
    _ => false,
}matches!(delegation.source, DelegationSource::Single);
394
395        let Some(block) = &delegation.body else { return Ok((should_generate_block, false)) };
396
397        // Report an error if user has explicitly specified delegation's target expression
398        // in a single delegation when reused function has no params.
399        if param_count == 0 && should_generate_block {
400            let err = DelegationBlockSpecifiedWhenNoParams { span: block.span };
401            return Err(tcx.dcx().emit_err(err));
402        }
403
404        struct DefinitionsFinder<'a, 'hir> {
405            resolver: &'a DelegationResolver<'a, 'hir>,
406        }
407
408        impl<'a> Visitor<'a> for DefinitionsFinder<'a, '_> {
409            type Result = ControlFlow<()>;
410
411            fn visit_id(&mut self, id: NodeId) -> Self::Result {
412                match self.resolver.is_definition(id) {
413                    true => ControlFlow::Break(()),
414                    false => ControlFlow::Continue(()),
415                }
416            }
417        }
418
419        let mut collector = DefinitionsFinder { resolver: self };
420
421        let contains_defs = collector.visit_block(block).is_break();
422
423        // If there are definitions inside and we can't delete target expression, then report an error.
424        // FIXME(fn_delegation): support deletion of target expression with defs inside.
425        if should_generate_block || !contains_defs {
426            Ok((should_generate_block, contains_defs))
427        } else {
428            Err(tcx.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }))
429        }
430    }
431
432    fn create_sig_mapping(
433        &self,
434        delegation: &Delegation,
435        span: Span,
436        should_generate_block: bool,
437        parent: LocalDefId,
438        sig: ty::FnSig<'tcx>,
439        contains_defs: bool,
440    ) -> Result<SigMapping, ErrorGuaranteed> {
441        let mut mapping = SigMapping::default();
442        if should_generate_block {
443            mapping.arguments_to_map.insert(0);
444        }
445
446        if self.can_perform_self_mapping(delegation, parent, span) {
447            /// Finds `Self` generic param only in ADT or references, so we avoid cases like
448            /// `Self::Item` which will return true if `output.contains(...)` will be used.
449            struct SelfFinder;
450
451            impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for SelfFinder {
452                type Result = ControlFlow<()>;
453
454                fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
455                    match t.kind() {
456                        ty::Adt(_, args) => {
457                            if args
458                                .iter()
459                                .flat_map(|arg| arg.as_type())
460                                .any(|type_arg| type_arg.is_self_param())
461                            {
462                                return ControlFlow::Break(());
463                            }
464
465                            t.super_visit_with(self)
466                        }
467                        ty::Ref(_, ref_t, _) => {
468                            if ref_t.is_self_param() {
469                                return ControlFlow::Break(());
470                            }
471
472                            t.super_visit_with(self)
473                        }
474                        _ => ControlFlow::Continue(()),
475                    }
476                }
477            }
478
479            impl SelfFinder {
480                fn contains_self(t: Ty<'_>) -> bool {
481                    t.is_self_param() || t.visit_with(&mut SelfFinder).is_break()
482                }
483            }
484
485            mapping.map_return = SelfFinder::contains_self(sig.output());
486
487            let arguments_to_map = sig
488                .inputs()
489                .iter()
490                .enumerate()
491                .skip(1) // Already checked above.
492                .filter_map(|(idx, &param)| SelfFinder::contains_self(param).then_some(idx));
493
494            mapping.arguments_to_map.extend(arguments_to_map);
495        }
496
497        // We can't yet map more than one argument if there are definitions inside.
498        // FIXME(fn_delegation): support relowering with defs inside
499        if contains_defs && mapping.arguments_to_map.len() > 1 {
500            let err = DelegationAttemptedBlockWithDefsRelowering { span };
501            let err = self.tcx().dcx().emit_err(err);
502            return Err(err);
503        }
504
505        Ok(mapping)
506    }
507
508    fn can_perform_self_mapping(
509        &self,
510        delegation: &Delegation,
511        parent: LocalDefId,
512        span: Span,
513    ) -> bool {
514        // Heuristic: don't do wrapping if there is no target expression.
515        if delegation.body.is_none() {
516            return false;
517        }
518
519        let tcx = self.tcx();
520
521        // Apply wrapping for delegations inside
522        // 1) Trait impls, as the return type of both signature function
523        //    and generated delegation has `Self` generic param returned
524        //    (checked below).
525        //    FIXME(fn_delegation): think of enabling wrapping in more scenarios:
526        //      trait-(impl)-to-free
527        //      trait-(impl)-to-inherent
528        //      inherent-to-free
529        // 2) Inherent methods when delegating to trait, as we change the type of
530        //    `Self` to type of struct or enum we delegate from.
531        if !#[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(parent) {
    DefKind::Impl { .. } => true,
    _ => false,
}matches!(tcx.def_kind(parent), DefKind::Impl { .. }) {
532            return false;
533        }
534
535        // Check that delegation path resolves to a trait AssocFn, not to a free method.
536        // After previous check we are sure that `sig_id` and `delegation.id`
537        // point to the same function.
538        let id = self
539            .get_call_path_res(delegation, span)
540            .ok()
541            .expect("invalid paths are filtered out earlier");
542
543        tcx.def_kind(id) == DefKind::AssocFn && tcx.def_kind(tcx.parent(id)) == DefKind::Trait
544    }
545}