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) -> Self {
        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: &Self) -> 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() -> Self {
        Self {
            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(_) => {
347                    Err(tcx.dcx().emit_err(UnresolvedDelegationCallee { span }))
348                }
349                TypeRelativeDelegationRes::Ambig(_) => {
350                    Err(tcx.dcx().emit_err(AmbiguousDelegationToInherentImpl { span }))
351                }
352            },
353            None => Err(self.create_unresolved_error(def_id, span)),
354        }
355    }
356
357    fn check_for_cycles(&self, mut def_id: DefId, span: Span) -> Result<(), ErrorGuaranteed> {
358        let tcx = self.tcx();
359        let mut visited: FxHashSet<DefId> = Default::default();
360        let delegation_infos = &tcx.resolutions(()).delegation_infos;
361
362        loop {
363            visited.insert(def_id);
364
365            // If def_id is in local crate and it corresponds to another delegation
366            // it means that we refer to another delegation as a callee, so in order to obtain
367            // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it.
368            if let Some(local_id) = def_id.as_local()
369                && delegation_infos.contains_key(&local_id)
370                && let Ok(id) = self.resolve_delegation_sig(local_id, span)
371            {
372                def_id = id;
373                if visited.contains(&def_id) {
374                    return Err(match visited.len() {
375                        1 => tcx.dcx().emit_err(UnresolvedDelegationCallee { span }),
376                        _ => tcx.dcx().emit_err(CycleInDelegationSignatureResolution { span }),
377                    });
378                }
379            } else {
380                return Ok(());
381            }
382        }
383    }
384
385    fn check_block_soundness(
386        &self,
387        delegation: &Delegation,
388        sig_id: DefId,
389        is_method: bool,
390        param_count: usize,
391    ) -> Result<(/* should generate block */ bool, /* contains defs */ bool), ErrorGuaranteed> {
392        let tcx = self.tcx();
393        let should_generate_block = is_method
394            || #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(sig_id) {
    DefKind::Fn => true,
    _ => false,
}matches!(tcx.def_kind(sig_id), DefKind::Fn)
395            || #[allow(non_exhaustive_omitted_patterns)] match delegation.source {
    DelegationSource::Single => true,
    _ => false,
}matches!(delegation.source, DelegationSource::Single);
396
397        let Some(block) = &delegation.body else { return Ok((should_generate_block, false)) };
398
399        // Report an error if user has explicitly specified delegation's target expression
400        // in a single delegation when reused function has no params.
401        if param_count == 0 && should_generate_block {
402            let err = DelegationBlockSpecifiedWhenNoParams { span: block.span };
403            return Err(tcx.dcx().emit_err(err));
404        }
405
406        struct DefinitionsFinder<'a, 'hir> {
407            resolver: &'a DelegationResolver<'a, 'hir>,
408        }
409
410        impl<'a> Visitor<'a> for DefinitionsFinder<'a, '_> {
411            type Result = ControlFlow<()>;
412
413            fn visit_id(&mut self, id: NodeId) -> Self::Result {
414                match self.resolver.is_definition(id) {
415                    true => ControlFlow::Break(()),
416                    false => ControlFlow::Continue(()),
417                }
418            }
419        }
420
421        let mut collector = DefinitionsFinder { resolver: self };
422
423        let contains_defs = collector.visit_block(block).is_break();
424
425        // If there are definitions inside and we can't delete target expression, then report an error.
426        // FIXME(fn_delegation): support deletion of target expression with defs inside.
427        if should_generate_block || !contains_defs {
428            Ok((should_generate_block, contains_defs))
429        } else {
430            Err(tcx.dcx().emit_err(DelegationAttemptedBlockWithDefsDeletion { span: block.span }))
431        }
432    }
433
434    fn create_sig_mapping(
435        &self,
436        delegation: &Delegation,
437        span: Span,
438        should_generate_block: bool,
439        parent: LocalDefId,
440        sig: ty::FnSig<'tcx>,
441        contains_defs: bool,
442    ) -> Result<SigMapping, ErrorGuaranteed> {
443        let mut mapping = SigMapping::default();
444        if should_generate_block {
445            mapping.arguments_to_map.insert(0);
446        }
447
448        if self.can_perform_self_mapping(delegation, parent, span) {
449            /// Finds `Self` generic param only in ADT or references, so we avoid cases like
450            /// `Self::Item` which will return true if `output.contains(...)` will be used.
451            struct SelfFinder;
452
453            impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for SelfFinder {
454                type Result = ControlFlow<()>;
455
456                fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
457                    match t.kind() {
458                        ty::Adt(_, args) => {
459                            if args
460                                .iter()
461                                .flat_map(|arg| arg.as_type())
462                                .any(|type_arg| type_arg.is_self_param())
463                            {
464                                return ControlFlow::Break(());
465                            }
466
467                            t.super_visit_with(self)
468                        }
469                        ty::Ref(_, ref_t, _) => {
470                            if ref_t.is_self_param() {
471                                return ControlFlow::Break(());
472                            }
473
474                            t.super_visit_with(self)
475                        }
476                        _ => ControlFlow::Continue(()),
477                    }
478                }
479            }
480
481            impl SelfFinder {
482                fn contains_self(t: Ty<'_>) -> bool {
483                    t.is_self_param() || t.visit_with(&mut SelfFinder).is_break()
484                }
485            }
486
487            mapping.map_return = SelfFinder::contains_self(sig.output());
488
489            let arguments_to_map = sig
490                .inputs()
491                .iter()
492                .enumerate()
493                .skip(1) // Already checked above.
494                .filter_map(|(idx, &param)| SelfFinder::contains_self(param).then_some(idx));
495
496            mapping.arguments_to_map.extend(arguments_to_map);
497        }
498
499        // We can't yet map more than one argument if there are definitions inside.
500        // FIXME(fn_delegation): support relowering with defs inside
501        if contains_defs && mapping.arguments_to_map.len() > 1 {
502            let err = DelegationAttemptedBlockWithDefsRelowering { span };
503            let err = self.tcx().dcx().emit_err(err);
504            return Err(err);
505        }
506
507        Ok(mapping)
508    }
509
510    fn can_perform_self_mapping(
511        &self,
512        delegation: &Delegation,
513        parent: LocalDefId,
514        span: Span,
515    ) -> bool {
516        // Heuristic: don't do wrapping if there is no target expression.
517        if delegation.body.is_none() {
518            return false;
519        }
520
521        let tcx = self.tcx();
522
523        // Apply wrapping for delegations inside
524        // 1) Trait impls, as the return type of both signature function
525        //    and generated delegation has `Self` generic param returned
526        //    (checked below).
527        //    FIXME(fn_delegation): think of enabling wrapping in more scenarios:
528        //      trait-(impl)-to-free
529        //      trait-(impl)-to-inherent
530        //      inherent-to-free
531        // 2) Inherent methods when delegating to trait, as we change the type of
532        //    `Self` to type of struct or enum we delegate from.
533        if !#[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(parent) {
    DefKind::Impl { .. } => true,
    _ => false,
}matches!(tcx.def_kind(parent), DefKind::Impl { .. }) {
534            return false;
535        }
536
537        // Check that delegation path resolves to a trait AssocFn, not to a free method.
538        // After previous check we are sure that `sig_id` and `delegation.id`
539        // point to the same function.
540        let id = self
541            .get_call_path_res(delegation, span)
542            .ok()
543            .expect("invalid paths are filtered out earlier");
544
545        tcx.def_kind(id) == DefKind::AssocFn && tcx.def_kind(tcx.parent(id)) == DefKind::Trait
546    }
547}