Skip to main content

charon_lib/ast/items/
fun_decl.rs

1use crate::ast::*;
2use derive_generic_visitor::{Drive, DriveMut, DriveTwo};
3use macros::EnumIsA;
4use macros::VariantName;
5use serde_state::DeserializeState;
6use serde_state::SerializeState;
7
8/// A function definition
9#[derive(Debug, Clone)]
10#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
11pub struct FunDecl {
12    pub def_id: FunDeclId,
13    /// The meta data associated with the declaration.
14    pub item_meta: ItemMeta,
15    pub generics: GenericParams,
16    /// The signature contains the inputs/output types and ABI details.
17    pub signature: Box<FunSig>,
18    /// The function kind: "regular" function, trait method declaration, etc.
19    pub src: FunSource,
20    /// The function body.
21    pub body: Body,
22}
23
24/// A function signature.
25#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
26#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
27pub struct FunSig {
28    /// Is the function unsafe or not
29    pub is_unsafe: bool,
30    /// The calling convention of this function.
31    pub abi: Abi,
32    /// Whether this is a C-variadic function (its last parameter is `...`).
33    pub is_variadic: bool,
34    pub inputs: Vec<Ty>,
35    pub output: Ty,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39#[derive(VariantName, EnumIsA)]
40#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
41#[serde_state(stateless)]
42#[cfg_attr(feature = "charon_on_charon", charon::variants_prefix("Abi"))]
43pub enum Abi {
44    Rust,
45    C,
46    /// Rust's spelling for the ABI, e.g. "C-unwind" or "system".
47    Other(ustr::Ustr),
48}
49
50/// Where a given function came from.
51#[derive(Debug, Clone)]
52#[derive(SerializeState, DeserializeState, Drive, DriveMut, DriveTwo)]
53#[cfg_attr(feature = "charon_on_charon", charon::variants_suffix("Fun"))]
54pub enum FunSource {
55    /// A normal function.
56    Normal,
57    /// A synthetic function representing an ADT constructor.
58    AdtConstructor,
59    /// A default method in a trait declaration.
60    TraitDefault {
61        /// The trait declaration this item belongs to.
62        trait_ref: TraitDeclRef,
63        /// The method this corresponds to.
64        // TODO: also include method generics so we can recover a full `FnPtr::TraitMethod`
65        item_id: TraitMethodId,
66    },
67    /// A method in a trait implementation.
68    TraitImpl {
69        /// The trait implementation the method belongs to.
70        impl_ref: TraitImplRef,
71        /// The trait declaration that the impl block implements.
72        trait_ref: TraitDeclRef,
73        /// The method this corresponds to.
74        // TODO: also include method generics so we can recover a full `FnPtr::TraitMethod`
75        item_id: TraitMethodId,
76        /// True if the trait decl had a default implementation for this method and this item is a
77        /// copy of the default item.
78        reuses_default: bool,
79    },
80    /// Wraps a concrete implementation of a method into a function that takes `dyn Trait` as its
81    /// `Self` type. This shim casts the receiver to the known concrete type and calls the real
82    /// method.
83    VTableShim,
84    /// The initializer for a global.
85    GlobalInitializer(GlobalDeclRef),
86    /// A target-specific variant behind a `TargetDispatch` façade. The dispatcher is the function
87    /// with the `Body::TargetDispatch` body that dispatches to this function.
88    TargetDependent { dispatcher: FunDeclRef },
89}
90
91impl FunDecl {
92    /// Replace the generic parameters of this function with the ones given by the binder.
93    pub fn substitute_params(self, subst: Binder<GenericArgs>) -> Self {
94        let FunDecl {
95            def_id,
96            item_meta,
97            generics: _,
98            signature,
99            src,
100            body,
101        } = self;
102        let signature = signature.substitute(&subst.skip_binder);
103        let src = src.substitute(&subst.skip_binder);
104        let body = body.substitute(&subst.skip_binder);
105        FunDecl {
106            def_id,
107            item_meta,
108            generics: subst.params,
109            signature,
110            src,
111            body,
112        }
113    }
114
115    /// Whether this function is unsafe to call, because it is declared `unsafe` (safety.unsafe-call),
116    /// or because it is a safe `#[target_feature]` function (safety.unsafe-target-feature-call). This
117    /// may be overriden at the call site, if we can statically discharge the unsafety (see [`CallSafety`]).
118    pub fn is_unsafe_to_call(&self, _krate: &TranslatedCrate) -> bool {
119        self.signature.is_unsafe
120    }
121
122    /// Whether this function is unsafe to declare.
123    pub fn is_unsafe_to_declare(&self, _krate: &TranslatedCrate) -> bool {
124        // The initializer of a global is part of the global's declaration.
125        !matches!(self.src, FunSource::GlobalInitializer(_))
126            && self.item_meta.is_unsafe_to_declare()
127    }
128
129    /// The function pointer signature of this item, binding its late-bound regions.
130    pub fn as_fn_ptr(&self) -> RegionBinder<FunSig> {
131        struct RebindLateRegions<'a> {
132            late_regions: &'a IndexVec<RegionId, Option<RegionId>>,
133        }
134
135        impl VarsVisitor for RebindLateRegions<'_> {
136            fn visit_region_var(&mut self, var: RegionDbVar) -> Option<Region> {
137                let DeBruijnVar::Bound(depth, id) = var else {
138                    return None;
139                };
140                if depth == DeBruijnId::one() {
141                    self.late_regions[id].map(|id| Region::Var(DeBruijnVar::new_at_zero(id)))
142                } else {
143                    None
144                }
145            }
146        }
147
148        let mut regions = IndexVec::new();
149        let late_regions = self.generics.regions.map_ref(|param| {
150            if param.late_bound {
151                Some(regions.push_with(|index| RegionParam {
152                    index,
153                    ..param.clone()
154                }))
155            } else {
156                None
157            }
158        });
159        let mut signature = self.signature.as_ref().clone().move_under_binder();
160        signature.visit_vars(&mut RebindLateRegions {
161            late_regions: &late_regions,
162        });
163        RegionBinder {
164            regions,
165            // Late-bound regions are guaranteed to not be involved in any non-implied outlives
166            // relations.
167            regions_outlive: Vec::new(),
168            types_outlive: Vec::new(),
169            skip_binder: signature,
170        }
171    }
172}
173
174impl RegionBinder<FunSig> {
175    /// Infer how the bound regions of `self` appear in `target`. The returned binder binds the
176    /// same regions as `target`; other lifetimes may differ.
177    pub fn infer_region_mapping(&self, target: &Self) -> Option<RegionBinder<RegionArgs>> {
178        struct RegionMapping {
179            regions: IndexVec<RegionId, Option<Region>>,
180            depth: DeBruijnId,
181        }
182
183        impl Visitor for RegionMapping {
184            type Break = ();
185        }
186
187        impl ZipAst for RegionMapping {
188            fn visit_region_param(
189                &mut self,
190                _source: &RegionParam,
191                _target: &RegionParam,
192            ) -> ControlFlow<()> {
193                // Binder positions identify regions; names and metadata need not match.
194                ControlFlow::Continue(())
195            }
196
197            fn visit_region(&mut self, source: &Region, target: &Region) -> ControlFlow<()> {
198                // We're only concerned with mapping bound variables.
199                if let Region::Var(var @ DeBruijnVar::Bound(depth, _)) = source
200                    && *depth <= self.depth
201                {
202                    if let Some(id) = var.bound_at_depth(self.depth) {
203                        let Some(target) = (*target).move_from_under_binders(self.depth) else {
204                            return ControlFlow::Break(());
205                        };
206                        let mapped = &mut self.regions[id];
207                        if let Some(previous) = mapped
208                            && *previous != target
209                        {
210                            return ControlFlow::Break(());
211                        }
212                        *mapped = Some(target);
213                        ControlFlow::Continue(())
214                    } else if source != target {
215                        ControlFlow::Break(())
216                    } else {
217                        ControlFlow::Continue(())
218                    }
219                } else {
220                    ControlFlow::Continue(())
221                }
222            }
223
224            fn visit_region_binder<T: AstVisitable>(
225                &mut self,
226                source: &RegionBinder<T>,
227                target: &RegionBinder<T>,
228            ) -> ControlFlow<()> {
229                self.depth = self.depth.incr();
230                let result = self.visit_inner(source, target);
231                self.depth = self.depth.decr();
232                result
233            }
234        }
235
236        let mut mapping = RegionMapping {
237            regions: self.regions.map_ref_indexed(|_, _| None),
238            depth: DeBruijnId::zero(),
239        };
240        if ZipAst::visit(&mut mapping, &self.skip_binder, &target.skip_binder).is_break() {
241            return None;
242        }
243        let regions = RegionArgs::new(mapping.regions.into_iter().collect::<Option<_>>()?);
244        Some(RegionBinder {
245            regions: target.regions.clone(),
246            regions_outlive: target.regions_outlive.clone(),
247            types_outlive: target.types_outlive.clone(),
248            skip_binder: regions,
249        })
250    }
251}
252
253impl Abi {
254    pub fn rust() -> Self {
255        Self::Rust
256    }
257
258    pub fn rust_name(&self) -> &str {
259        match self {
260            Self::Rust => "Rust",
261            Self::C => "C",
262            Self::Other(name) => name.as_str(),
263        }
264    }
265}