| // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| // Exceptions. See /LICENSE for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| extern crate rustc_span; |
| |
| use crate::FineGrainedFeature; |
| use arc_anyhow::Result; |
| use code_gen_utils::CcInclude; |
| use crubit_abi_type::CrubitAbiType; |
| use error_report::bail; |
| use itertools::Itertools; |
| use proc_macro2::TokenStream; |
| use rustc_span::def_id::DefId; |
| use rustc_span::Symbol; |
| use std::cmp::Ordering; |
| use std::collections::BTreeSet; |
| use std::collections::HashSet; |
| use std::fmt; |
| use std::ops::{Add, AddAssign}; |
| |
| /// A CrubitAbiType that has C++ prerequisites. |
| /// This type just serves as a named pair for CrubitAbiType and CcPrerequisites, and is useful |
| /// for when we build up a CrubitAbiType where some of the inner types (e.g. std::string_view) |
| /// might have C++ prerequisites (e.g. <string>) that need to be included to work. |
| #[derive(Clone)] |
| pub struct CrubitAbiTypeWithCcPrereqs { |
| pub crubit_abi_type: CrubitAbiType, |
| pub prereqs: CcPrerequisites, |
| } |
| |
| impl CrubitAbiTypeWithCcPrereqs { |
| /// Extracts the CrubitAbiType and adds the C++ prerequisites to the given `prereqs`. |
| pub fn crubit_abi_type(self, prereqs: &mut CcPrerequisites) -> CrubitAbiType { |
| *prereqs += self.prereqs; |
| self.crubit_abi_type |
| } |
| } |
| impl From<CrubitAbiType> for CrubitAbiTypeWithCcPrereqs { |
| fn from(crubit_abi_type: CrubitAbiType) -> Self { |
| Self { crubit_abi_type, prereqs: Default::default() } |
| } |
| } |
| |
| #[derive(Clone, Debug, Default)] |
| pub struct CcPrerequisites { |
| /// Set of `#include`s that a `CcSnippet` depends on. For example if |
| /// `CcSnippet::tokens` expands to `std::int32_t`, then `includes` |
| /// need to cover the `#include <cstdint>`. |
| pub includes: BTreeSet<CcInclude>, |
| |
| /// Set of local definitions that a `CcSnippet` depends on. For example if |
| /// `CcSnippet::tokens` expands to `void foo(S s) { ... }` then the |
| /// definition of `S` should have appeared earlier - in this case `defs` |
| /// will include the `DefId` corresponding to `S`. Note that the |
| /// definition of `S` is covered by `ApiSnippets::main_api` (i.e. the |
| /// predecessor of a toposort edge is `ApiSnippets::main_api` - it is not |
| /// possible to depend on `ApiSnippets::cc_details`). |
| pub defs: HashSet<DefId>, |
| |
| /// Set of forward declarations that a `CcSnippet` depends on. For example |
| /// if `CcSnippet::tokens` expands to `void foo(S* s)` then a forward |
| /// declaration of `S` should have appeared earlier - in this case |
| /// `fwd_decls` will include the `DefId` corresponding to `S`. |
| /// Note that in this particular example the *definition* of `S` does |
| /// *not* need to appear earlier (and therefore `defs` will *not* |
| /// contain `DefId` corresponding to `S`). |
| pub fwd_decls: HashSet<DefId>, |
| |
| /// Set of Crubit feature flags required for the CcSnippet to be valid. |
| pub required_features: flagset::FlagSet<FineGrainedFeature>, |
| } |
| |
| impl CcPrerequisites { |
| pub fn is_empty(&self) -> bool { |
| let Self { includes, defs, fwd_decls, required_features } = self; |
| includes.is_empty() |
| && defs.is_empty() |
| && fwd_decls.is_empty() |
| && required_features.is_empty() |
| } |
| |
| /// Weakens all dependencies to only require a forward declaration. Example |
| /// usage scenarios: |
| /// - Computing prerequisites of pointer types (the pointee type can just be |
| /// forward-declared), |
| /// - Computing prerequisites of function declarations (parameter types and |
| /// return type can just be forward-declared). |
| pub fn move_defs_to_fwd_decls(&mut self) { |
| self.fwd_decls.extend(std::mem::take(&mut self.defs)) |
| } |
| } |
| |
| impl AddAssign for CcPrerequisites { |
| #[allow(clippy::suspicious_op_assign_impl)] |
| fn add_assign(&mut self, rhs: Self) { |
| let Self { mut includes, defs, fwd_decls, required_features } = rhs; |
| |
| // `BTreeSet::append` is used because it _seems_ to be more efficient than |
| // calling `extend`. This is because `extend` takes an iterator |
| // (processing each `rhs` include one-at-a-time) while `append` steals |
| // the whole backing data store from `rhs.includes`. OTOH, this is a bit |
| // speculative, since the (expected / guessed) performance difference is |
| // not documented at |
| // https://doc.rust-lang.org/std/collections/struct.BTreeSet.html#method.append |
| self.includes.append(&mut includes); |
| |
| self.defs.extend(defs); |
| self.fwd_decls.extend(fwd_decls); |
| self.required_features |= required_features; |
| } |
| } |
| |
| impl Add for CcPrerequisites { |
| type Output = Self; |
| |
| fn add(mut self, rhs: Self) -> Self { |
| self += rhs; |
| self |
| } |
| } |
| |
| #[derive(Clone, Default)] |
| pub struct CcSnippet { |
| pub tokens: TokenStream, |
| pub prereqs: CcPrerequisites, |
| } |
| |
| // Override debug to use the Display impl for tokens, as the Debug impl for TokenStream is rarely |
| // useful (it shows the structure of the tokens, not the actual text). |
| impl fmt::Debug for CcSnippet { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
| f.debug_struct("CcSnippet") |
| .field("tokens", &self.tokens.to_string()) |
| .field("prereqs", &self.prereqs) |
| .finish() |
| } |
| } |
| |
| impl CcSnippet { |
| /// Consumes `self` and returns its `tokens`, while preserving |
| /// its `prereqs` into `prereqs_accumulator`. |
| pub fn into_tokens(self, prereqs_accumulator: &mut CcPrerequisites) -> TokenStream { |
| let Self { tokens, prereqs } = self; |
| *prereqs_accumulator += prereqs; |
| tokens |
| } |
| |
| /// Creates a new CcSnippet (with no `CcPrerequisites`). |
| pub fn new(tokens: TokenStream) -> Self { |
| Self { tokens, ..Default::default() } |
| } |
| |
| /// Creates a CcSnippet that depends on a single `CcInclude`. |
| pub fn with_include(tokens: TokenStream, include: CcInclude) -> Self { |
| let mut prereqs = CcPrerequisites::default(); |
| prereqs.includes.insert(include); |
| Self { tokens, prereqs } |
| } |
| |
| /// Resolves the feature requirements. If the required features of `self` |
| /// are in `crubit_features`, then this returns a version of `self` with |
| /// the feature requirements removed. Otherwise, this returns an error. |
| pub fn resolve_feature_requirements( |
| mut self, |
| crubit_features: flagset::FlagSet<crubit_feature::CrubitFeature>, |
| ) -> Result<Self> { |
| let mut errs = Vec::new(); |
| for feature in self.prereqs.required_features { |
| if let Err(e) = feature.ensure_crubit_feature(crubit_features) { |
| errs.push(e); |
| } |
| } |
| match errs.len() { |
| 0 => { |
| self.prereqs.required_features.clear(); |
| Ok(self) |
| } |
| 1 => Err(errs.pop().unwrap()), |
| _ => { |
| let mut errs = errs.into_iter().map(|e| e.to_string()); |
| bail!(errs.join(", ")) |
| } |
| } |
| } |
| } |
| |
| impl AddAssign for CcSnippet { |
| fn add_assign(&mut self, rhs: Self) { |
| self.tokens.extend(rhs.into_tokens(&mut self.prereqs)); |
| } |
| } |
| |
| /// Holds the declaration of an extern "C" function. |
| /// |
| /// ADTs can be annotated with conversion functions that adhere to the C calling |
| /// conventions. Crubit needs to declare these functions within an extern "C" |
| /// block, so rustc knows that the conversion function is defined elsewhere. |
| /// |
| /// This type implements PartialEq and PartialOrd based on the `symbol` field. |
| /// The `decl` field is ignored. All `ExternCDecl` instances from all thunks are |
| /// eventually put in BTreeSet to remove duplicates and to achieve deterministic |
| /// ordering of the generated extern "C" { ... } block. |
| #[derive(Clone, Debug)] |
| pub struct ExternCDecl { |
| /// The name of the function. |
| pub symbol: Symbol, |
| |
| /// The full function declaration as can be placed in an extern "C" block. |
| pub decl: TokenStream, |
| } |
| |
| impl Ord for ExternCDecl { |
| fn cmp(&self, other: &Self) -> Ordering { |
| self.symbol.cmp(&other.symbol) |
| } |
| } |
| |
| impl PartialOrd for ExternCDecl { |
| fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
| Some(self.cmp(other)) |
| } |
| } |
| |
| impl PartialEq for ExternCDecl { |
| fn eq(&self, other: &Self) -> bool { |
| self.symbol == other.symbol |
| } |
| } |
| |
| impl Eq for ExternCDecl {} |
| |
| #[derive(Clone, Default)] |
| pub struct RsSnippet { |
| pub tokens: TokenStream, |
| |
| /// Set of extern "C" declarations needed by `tokens`. |
| pub extern_c_decls: BTreeSet<ExternCDecl>, |
| } |
| |
| // Override debug to use the Display impl for tokens, as the Debug impl for TokenStream is rarely |
| // useful (it shows the structure of the tokens, not the actual text). |
| impl fmt::Debug for RsSnippet { |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
| f.debug_struct("RsSnippet") |
| .field("tokens", &self.tokens.to_string()) |
| .field("extern_c_decls", &self.extern_c_decls) |
| .finish() |
| } |
| } |
| |
| impl FromIterator<RsSnippet> for RsSnippet { |
| fn from_iter<I: IntoIterator<Item = RsSnippet>>(iter: I) -> Self { |
| let mut result = RsSnippet::default(); |
| for RsSnippet { tokens, extern_c_decls } in iter.into_iter() { |
| result.tokens.extend(tokens); |
| result.extern_c_decls.extend(extern_c_decls); |
| } |
| result |
| } |
| } |
| |
| impl RsSnippet { |
| // Creates a new RsSnippet from a TokenStream. |
| pub fn new(tokens: TokenStream) -> Self { |
| Self { tokens, ..Default::default() } |
| } |
| |
| /// Consumes `self` and returns its `tokens`, while preserving |
| /// its `extern_c_decls` into `extern_c_decls_accumulator`. |
| pub fn into_tokens( |
| self, |
| extern_c_decls_accumulator: &mut BTreeSet<ExternCDecl>, |
| ) -> TokenStream { |
| extern_c_decls_accumulator.extend(self.extern_c_decls); |
| self.tokens |
| } |
| } |
| |
| impl AddAssign for RsSnippet { |
| fn add_assign(&mut self, rhs: Self) { |
| self.tokens.extend(rhs.tokens); |
| self.extern_c_decls.extend(rhs.extern_c_decls); |
| } |
| } |
| |
| #[derive(Clone, Debug, Default)] |
| pub struct ApiSnippets { |
| /// Main API - for example: |
| /// - A C++ declaration of a function (with a doc comment), |
| /// - A C++ definition of a struct (with a doc comment). |
| pub main_api: CcSnippet, |
| |
| /// C++ implementation details - for example: |
| /// - A C++ declaration of an `extern "C"` thunk, |
| /// - C++ `static_assert`s about struct size, aligment, and field offsets. |
| pub cc_details: CcSnippet, |
| |
| /// Rust implementation details - for example: |
| /// - A Rust implementation of an `extern "C"` thunk, |
| /// - Rust `assert!`s about struct size, aligment, and field offsets. |
| pub rs_details: RsSnippet, |
| } |
| |
| impl ApiSnippets { |
| /// Resolves the feature requirements. If the required features of `self` |
| /// are in `crubit_features`, then this returns a version of `self` with |
| /// the feature requirements removed. Otherwise, this returns an error. |
| pub fn resolve_feature_requirements( |
| self, |
| crubit_features: flagset::FlagSet<crubit_feature::CrubitFeature>, |
| ) -> Result<Self> { |
| Ok(Self { |
| main_api: self.main_api.resolve_feature_requirements(crubit_features)?, |
| cc_details: self.cc_details.resolve_feature_requirements(crubit_features)?, |
| rs_details: self.rs_details, |
| }) |
| } |
| } |
| |
| impl FromIterator<ApiSnippets> for ApiSnippets { |
| fn from_iter<I: IntoIterator<Item = ApiSnippets>>(iter: I) -> Self { |
| let mut result = ApiSnippets::default(); |
| for ApiSnippets { main_api, cc_details, rs_details } in iter.into_iter() { |
| result.main_api += main_api; |
| result.cc_details += cc_details; |
| result.rs_details += rs_details; |
| } |
| result |
| } |
| } |