| // 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 |
| |
| // This file defines an intermediate representation (IR) used between Clang AST |
| // and code generators that generate Rust bindings and C++ bindings |
| // implementation. |
| // |
| // All types in this file own their data. This IR is expected to outlive the |
| // Clang's AST context, therefore it cannot reference data owned by it. |
| #ifndef CRUBIT_RS_BINDINGS_FROM_CC_IR_H_ |
| #define CRUBIT_RS_BINDINGS_FROM_CC_IR_H_ |
| |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "third_party/absl/strings/string_view.h" |
| #include "third_party/json/src/json.hpp" |
| |
| namespace rs_bindings_from_cc { |
| |
| namespace internal { |
| inline constexpr absl::string_view kRustPtrMut = "*mut"; |
| inline constexpr absl::string_view kRustPtrConst = "*const"; |
| inline constexpr absl::string_view kCcPtr = "*"; |
| } // namespace internal |
| |
| // A name of a public header of the C++ library. |
| class HeaderName { |
| public: |
| explicit HeaderName(std::string name) : name_(std::move(name)) {} |
| |
| absl::string_view IncludePath() const { return name_; } |
| |
| nlohmann::json ToJson() const; |
| |
| private: |
| // Header pathname in the format suitable for a google3-relative quote |
| // include. |
| std::string name_; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const HeaderName& h) { |
| return o << h.ToJson(); |
| } |
| |
| // A C++ type involved in the bindings. It has the knowledge of how the type |
| // is spelled in C++. |
| struct CcType { |
| nlohmann::json ToJson() const; |
| // The name of the type. For example, int or void. |
| std::string name; |
| |
| // The C++ const-qualification for the type. |
| // |
| // Note: there are two types for which cv-qualification does not do anything: |
| // references and functions. if `T` is either a function type like `void()`, |
| // or a reference type like `int&`, then `T`, `const T`, and `volatile T` are |
| // all the same type in C++. |
| bool is_const = false; |
| |
| // Type parameters for a generic type. Examples: |
| // int has no type parameters. |
| // int* has a single type parameter, int. |
| // tuple<int, float> has two type parameters, int and float. |
| std::vector<CcType> type_params = {}; |
| }; |
| |
| // A Rust type involved in the bindings. It has the knowledge of how the type |
| // is spelled in Rust. |
| struct RsType { |
| nlohmann::json ToJson() const; |
| |
| // The name of the type. For example, i32 or (). |
| std::string name; |
| |
| // Type parameters for a generic type. Examples: |
| // i32 has no type parameters. |
| // *mut i32 has a single type parameter, i32. |
| // (i32, f32) has two type parameters, i32 and f32. |
| std::vector<RsType> type_params = {}; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const RsType& type) { |
| return o << type.ToJson(); |
| } |
| |
| // A type involved in the bindings. The rs_type and cc_type will be treated |
| // as interchangeable during bindings, and so should share the same layout. |
| // |
| // For example: a C++ pointer may be a usize in Rust, rather than a pointer, but |
| // should almost certainly not be a u8, because u8 and pointers are sized and |
| // aligned differently. |
| struct MappedType { |
| static MappedType Void() { return Simple("()", "void"); } |
| |
| /// Returns the MappedType for a non-templated/generic, non-cv-qualified type. |
| /// For example, Void() is Simple("()", "void"). |
| static MappedType Simple(std::string rs_name, std::string cc_name) { |
| return MappedType{RsType{rs_name}, CcType{cc_name}}; |
| } |
| |
| static MappedType PointerTo(MappedType pointee_type) { |
| absl::string_view rs_name = pointee_type.cc_type.is_const |
| ? internal::kRustPtrConst |
| : internal::kRustPtrMut; |
| auto pointer_type = |
| Simple(std::string(rs_name), std::string(internal::kCcPtr)); |
| pointer_type.rs_type.type_params.push_back(std::move(pointee_type.rs_type)); |
| pointer_type.cc_type.type_params.push_back(std::move(pointee_type.cc_type)); |
| return pointer_type; |
| } |
| |
| bool IsVoid() const { return rs_type.name == "()"; } |
| |
| nlohmann::json ToJson() const; |
| |
| RsType rs_type; |
| CcType cc_type; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const MappedType& type) { |
| return o << type.ToJson(); |
| } |
| |
| // An identifier involved in bindings. |
| // |
| // Examples: |
| // Identifier of C++'s `int32_t Add(int32_t a, int32_t b)` will be |
| // `Identifier("add")`. |
| // |
| // Invariants: |
| // `identifier` cannot be empty. |
| class Identifier { |
| public: |
| explicit Identifier(std::string identifier) |
| : identifier_(std::move(identifier)) { |
| CHECK(!identifier_.empty()) << "Identifier name cannot be empty."; |
| } |
| |
| absl::string_view Ident() const { return identifier_; } |
| |
| nlohmann::json ToJson() const; |
| |
| private: |
| std::string identifier_; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const Identifier& id) { |
| return o << id.Ident(); |
| } |
| |
| // A function parameter. |
| // |
| // Examples: |
| // FuncParam of a C++ function `void Foo(int32_t a);` will be |
| // `FuncParam{.type=Type{"i32", "int32_t"}, .identifier=Identifier("foo"))`. |
| struct FuncParam { |
| nlohmann::json ToJson() const; |
| |
| MappedType type; |
| Identifier identifier; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const FuncParam& param) { |
| return o << param.ToJson(); |
| } |
| |
| // A function involved in the bindings. |
| struct Func { |
| nlohmann::json ToJson() const; |
| |
| Identifier identifier; |
| std::optional<std::string> doc_comment; |
| std::string mangled_name; |
| MappedType return_type; |
| std::vector<FuncParam> params; |
| bool is_inline; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const Func& f) { |
| return o << f.ToJson(); |
| } |
| |
| // Access specifier for a member or base class. |
| enum AccessSpecifier { |
| kPublic, |
| kProtected, |
| kPrivate, |
| }; |
| |
| std::ostream& operator<<(std::ostream& o, const AccessSpecifier& access); |
| |
| // A field (non-static member variable) of a record. |
| struct Field { |
| nlohmann::json ToJson() const; |
| |
| Identifier identifier; |
| std::optional<std::string> doc_comment; |
| MappedType type; |
| AccessSpecifier access; |
| // Field offset in bits. |
| uint64_t offset; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const Field& f) { |
| return o << f.ToJson(); |
| } |
| |
| /// Information about special member functions. |
| struct SpecialMemberFunc { |
| enum class Definition : char { |
| kTrivial, |
| kNontrivial, |
| kDeleted, |
| }; |
| |
| nlohmann::json ToJson() const; |
| |
| Definition definition = Definition::kTrivial; |
| AccessSpecifier access = AccessSpecifier::kPublic; |
| }; |
| |
| std::ostream& operator<<(std::ostream& o, |
| const SpecialMemberFunc::Definition& definition); |
| |
| inline std::ostream& operator<<(std::ostream& o, const SpecialMemberFunc& f) { |
| return o << f.ToJson(); |
| } |
| |
| // A record (struct, class, union). |
| struct Record { |
| nlohmann::json ToJson() const; |
| |
| Identifier identifier; |
| std::optional<std::string> doc_comment; |
| std::vector<Field> fields; |
| // Size and alignment in bytes. |
| int64_t size; |
| int64_t alignment; |
| |
| // Special member functions. |
| SpecialMemberFunc copy_constructor = {}; |
| SpecialMemberFunc move_constructor = {}; |
| SpecialMemberFunc destructor = {}; |
| |
| // Whether this type is passed by value as if it were a trivial type (the same |
| // as it would be if it were a struct in C). |
| // |
| // This can be either due to language rules (it *is* a trivial type), or due |
| // to the usage of a Clang attribute that forces trivial for calls: |
| // |
| // * https://eel.is/c++draft/class.temporary#3 |
| // * https://clang.llvm.org/docs/AttributeReference.html#trivial-abi |
| bool is_trivial_abi = false; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const Record& r) { |
| return o << r.ToJson(); |
| } |
| |
| // Source code location |
| struct SourceLoc { |
| nlohmann::json ToJson() const; |
| |
| std::string filename; |
| uint64 line; |
| uint64 column; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const SourceLoc& r) { |
| return o << r.ToJson(); |
| } |
| |
| // A placeholder for an item that we can't generate bindings for (yet) |
| struct UnsupportedItem { |
| nlohmann::json ToJson() const; |
| |
| // TODO(forster): We could show the original declaration in the generated |
| // message (potentially also for successfully imported items). |
| |
| // Qualified name of the item for which we couldn't generate bindings |
| std::string name; |
| |
| // Explanation of why we couldn't generate bindings |
| // TODO(forster): We should support multiple reasons per unsupported item. |
| std::string message; |
| SourceLoc source_loc; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const UnsupportedItem& r) { |
| return o << r.ToJson(); |
| } |
| |
| struct Comment { |
| nlohmann::json ToJson() const; |
| |
| std::string text; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const Comment& r) { |
| return o << r.ToJson(); |
| } |
| |
| // A complete intermediate representation of bindings for publicly accessible |
| // declarations of a single C++ library. |
| struct IR { |
| nlohmann::json ToJson() const; |
| |
| template <typename T> |
| std::vector<T*> get_items_if() { |
| std::vector<T*> filtered_items; |
| for (auto& item : items) { |
| if (auto* filtered_item = std::get_if<T>(&item)) { |
| filtered_items.push_back(filtered_item); |
| } |
| } |
| return filtered_items; |
| } |
| |
| // Collection of public headers that were used to construct the AST this `IR` |
| // is generated from. |
| std::vector<HeaderName> used_headers; |
| std::vector<std::variant<Func, Record, UnsupportedItem, Comment>> items; |
| }; |
| |
| inline std::ostream& operator<<(std::ostream& o, const IR& ir) { |
| return o << ir.ToJson(); |
| } |
| |
| } // namespace rs_bindings_from_cc |
| |
| #endif // CRUBIT_RS_BINDINGS_FROM_CC_IR_H_ |