blob: c87cdec990ac15db00c45d1f13185a90ca04dde4 [file] [log] [blame] [edit]
// 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
#include "rs_bindings_from_cc/ir.h"
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <utility>
#include <variant>
#include <vector>
#include "absl/base/nullability.h"
#include "absl/log/check.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/string_type.h"
#include "common/strong_int.h"
#include "llvm/Support/JSON.h"
namespace crubit {
namespace {
// https://en.cppreference.com/w/cpp/utility/variant/visit
template <typename... Ts>
struct visitor : Ts... {
using Ts::operator()...;
};
} // namespace
template <class T>
llvm::json::Value toJSON(const T& t) {
return t.ToJson();
}
template <typename TTag, typename TInt>
llvm::json::Value toJSON(const crubit::StrongInt<TTag, TInt> strong_int) {
return llvm::json::Value(strong_int.value());
}
template <typename TTag>
llvm::json::Value toJSON(const crubit::StringType<TTag> string_type) {
return llvm::json::Value(string_type.value());
}
template <class T>
llvm::json::Value toJSON(const absl::StatusOr<T>& t) {
if (t.ok()) {
return llvm::json::Object{{"Ok", *t}};
}
return llvm::json::Object{{"Err", std::string(t.status().message())}};
}
llvm::json::Value HeaderName::ToJson() const {
return llvm::json::Object{
{"name", name_},
};
}
llvm::json::Value LifetimeName::ToJson() const {
return llvm::json::Object{
{"name", name},
{"id", id},
};
}
llvm::json::Value CcType::ToJson() const {
llvm::json::Object variant_object = std::visit(
visitor{
[&](CcType::Primitive primitive) {
return llvm::json::Object{{"Primitive", primitive.spelling}};
},
[&](CcType::PointerType pointer) {
return llvm::json::Object{
{"Pointer",
llvm::json::Object{
{
"kind",
[&]() -> llvm::json::Value {
switch (pointer.kind) {
case PointerTypeKind::kLValueRef:
return "LValueRef";
case PointerTypeKind::kRValueRef:
return "RValueRef";
case PointerTypeKind::kNullable:
return "Nullable";
case PointerTypeKind::kNonNull:
return "NonNull";
}
}(),
},
{"lifetime", pointer.lifetime},
{"pointee_type", *pointer.pointee_type},
}},
};
},
[&](const CcType::FuncPointer& func_value) {
std::vector<llvm::json::Value> param_and_return_type_values;
param_and_return_type_values.reserve(
func_value.param_and_return_types.size());
for (const CcType& type : func_value.param_and_return_types) {
param_and_return_type_values.push_back(type.ToJson());
}
return llvm::json::Object{
{"FuncPointer",
llvm::json::Object{
{"non_null", func_value.non_null},
{
"call_conv",
[&]() -> llvm::json::Value {
switch (func_value.call_conv) {
case CallingConv::kC:
return "cdecl";
case CallingConv::kX86FastCall:
return "fastcall";
case CallingConv::kX86VectorCall:
return "vectorcall";
case CallingConv::kX864ThisCall:
return "thiscall";
case CallingConv::kX86StdCall:
return "stdcall";
case CallingConv::kWin64:
return "ms_abi";
}
}(),
},
{"param_and_return_types", param_and_return_type_values},
}},
};
},
[&](ItemId id) { return llvm::json::Object{{"Decl", id}}; }},
variant);
return llvm::json::Object{
{"variant", std::move(variant_object)},
{"is_const", is_const},
{"unknown_attr", unknown_attr},
};
}
namespace {
CcType PointerOrReferenceTo(CcType pointee_type, PointerTypeKind pointer_kind,
std::optional<LifetimeId> lifetime) {
return CcType(CcType::PointerType{
.kind = pointer_kind,
.lifetime = lifetime,
.pointee_type = std::make_shared<CcType>(std::move(pointee_type)),
});
}
} // namespace
CcType CcType::PointerTo(CcType pointee_type,
std::optional<LifetimeId> lifetime, bool nullable) {
return PointerOrReferenceTo(
std::move(pointee_type),
nullable ? PointerTypeKind::kNullable : PointerTypeKind::kNonNull,
lifetime);
}
CcType CcType::LValueReferenceTo(CcType pointee_type,
std::optional<LifetimeId> lifetime) {
return PointerOrReferenceTo(std::move(pointee_type),
PointerTypeKind::kLValueRef, lifetime);
}
CcType CcType::RValueReferenceTo(CcType pointee_type,
std::optional<LifetimeId> lifetime) {
return PointerOrReferenceTo(std::move(pointee_type),
PointerTypeKind::kRValueRef, lifetime);
}
llvm::json::Value Identifier::ToJson() const {
return llvm::json::Object{
{"identifier", identifier_},
};
}
llvm::json::Value IntegerConstant::ToJson() const {
return llvm::json::Object{
{"is_negative", is_negative_},
{"wrapped_value", wrapped_value_},
};
}
llvm::json::Value Operator::ToJson() const {
return llvm::json::Object{
{"name", name_},
};
}
static std::string SpecialNameToString(SpecialName special_name) {
switch (special_name) {
case SpecialName::kDestructor:
return "Destructor";
case SpecialName::kConstructor:
return "Constructor";
}
}
llvm::json::Value toJSON(const UnqualifiedIdentifier& unqualified_identifier) {
if (auto* id = std::get_if<Identifier>(&unqualified_identifier)) {
return llvm::json::Object{
{"Identifier", *id},
};
} else if (auto* op = std::get_if<Operator>(&unqualified_identifier)) {
return llvm::json::Object{
{"Operator", *op},
};
} else {
SpecialName special_name = std::get<SpecialName>(unqualified_identifier);
return llvm::json::Object{
{SpecialNameToString(special_name), nullptr},
};
}
}
llvm::json::Value FuncParam::ToJson() const {
return llvm::json::Object{
{"type", type},
{"identifier", identifier},
{"unknown_attr", unknown_attr},
};
}
std::ostream& operator<<(std::ostream& o, const SpecialName& special_name) {
return o << SpecialNameToString(special_name);
}
UnqualifiedIdentifier& TranslatedUnqualifiedIdentifier::rs_identifier() {
if (crubit_rust_name.has_value()) {
return *crubit_rust_name;
}
return cc_identifier;
}
Identifier& TranslatedIdentifier::rs_identifier() {
if (crubit_rust_name.has_value()) {
return *crubit_rust_name;
}
return cc_identifier;
}
llvm::json::Value MemberFuncMetadata::InstanceMethodMetadata::ToJson() const {
const char* reference_str = nullptr;
switch (reference) {
case MemberFuncMetadata::kLValue:
reference_str = "LValue";
break;
case MemberFuncMetadata::kRValue:
reference_str = "RValue";
break;
case MemberFuncMetadata::kUnqualified:
reference_str = "Unqualified";
break;
}
return llvm::json::Object{
{"reference", reference_str},
{"is_const", is_const},
{"is_virtual", is_virtual},
};
}
llvm::json::Value MemberFuncMetadata::ToJson() const {
return llvm::json::Object{
{"record_id", record_id},
{"instance_method_metadata", instance_method_metadata},
};
}
llvm::json::Value ExistingRustType::ToJson() const {
llvm::json::Object override{
{"rs_name", rs_name},
{"cc_name", cc_name},
{"type_parameters", type_parameters},
{"owning_target", owning_target},
{"is_same_abi", is_same_abi},
{"id", id},
{"must_bind", must_bind},
};
if (size_align.has_value()) {
override.insert({"size_align", size_align->ToJson()});
}
return llvm::json::Object{
{"ExistingRustType", std::move(override)},
};
}
llvm::json::Value UseMod::ToJson() const {
llvm::json::Object use_mod{
{"path", path},
{"mod_name", mod_name},
{"id", id},
{"must_bind", must_bind},
};
return llvm::json::Object{
{"UseMod", std::move(use_mod)},
};
}
static std::string SafetyAnnotationToString(
SafetyAnnotation safety_annotation) {
switch (safety_annotation) {
case SafetyAnnotation::kDisableUnsafe:
return "DisableUnsafe";
case SafetyAnnotation::kUnsafe:
return "Unsafe";
case SafetyAnnotation::kUnannotated:
return "Unannotated";
}
}
llvm::json::Value Func::ToJson() const {
llvm::json::Object func{
{"cc_name", cc_name},
{"rs_name", rs_name},
{"owning_target", owning_target},
{"doc_comment", doc_comment},
{"mangled_name", mangled_name},
{"return_type", return_type},
{"params", params},
{"lifetime_params", lifetime_params},
{"is_inline", is_inline},
{"member_func_metadata", member_func_metadata},
{"is_extern_c", is_extern_c},
{"is_noreturn", is_noreturn},
{"is_variadic", is_variadic},
{"is_consteval", is_consteval},
{"nodiscard", nodiscard},
{"deprecated", deprecated},
{"has_c_calling_convention", has_c_calling_convention},
{"is_member_or_descendant_of_class_template",
is_member_or_descendant_of_class_template},
{"safety_annotation", SafetyAnnotationToString(safety_annotation)},
{"source_loc", source_loc},
{"id", id},
{"enclosing_item_id", enclosing_item_id},
{"adl_enclosing_record", adl_enclosing_record},
{"must_bind", must_bind},
};
return llvm::json::Object{
{"Func", std::move(func)},
};
}
static std::string AccessToString(AccessSpecifier access) {
switch (access) {
case kPublic:
return "Public";
case kProtected:
return "Protected";
case kPrivate:
return "Private";
}
}
std::ostream& operator<<(std::ostream& o, const AccessSpecifier& access) {
return o << AccessToString(access);
}
llvm::json::Value Field::ToJson() const {
return llvm::json::Object{
{"rust_identifier", rust_identifier},
{"cpp_identifier", cpp_identifier},
{"doc_comment", doc_comment},
{"type", type},
{"access", AccessToString(access)},
{"offset", offset},
{"size", size},
{"unknown_attr", toJSON(unknown_attr)},
{"is_no_unique_address", is_no_unique_address},
{"is_bitfield", is_bitfield},
{"is_inheritable", is_inheritable},
};
}
llvm::json::Value toJSON(const SpecialMemberFunc& f) {
switch (f) {
case SpecialMemberFunc::kTrivial:
return "Trivial";
case SpecialMemberFunc::kNontrivialMembers:
return "NontrivialMembers";
case SpecialMemberFunc::kNontrivialUserDefined:
return "NontrivialUserDefined";
case SpecialMemberFunc::kUnavailable:
return "Unavailable";
}
}
llvm::json::Value BaseClass::ToJson() const {
return llvm::json::Object{
{"base_record_id", base_record_id},
{"offset", offset},
};
}
static std::string RecordTypeToString(RecordType record_type) {
switch (record_type) {
case kStruct:
return "Struct";
case kUnion:
return "Union";
case kClass:
return "Class";
}
}
std::ostream& operator<<(std::ostream& o, const RecordType& record_type) {
return o << RecordTypeToString(record_type);
}
llvm::json::Value IncompleteRecord::ToJson() const {
llvm::json::Object record{{"cc_name", cc_name},
{"rs_name", rs_name},
{"id", id},
{"owning_target", owning_target},
{"unknown_attr", unknown_attr},
{"record_type", RecordTypeToString(record_type)},
{"enclosing_item_id", enclosing_item_id},
{"must_bind", must_bind}};
return llvm::json::Object{
{"IncompleteRecord", std::move(record)},
};
}
llvm::json::Value SizeAlign::ToJson() const {
return llvm::json::Object{
{"size", size},
{"alignment", alignment},
};
}
llvm::json::Value BridgeType::ToJson() const {
return std::visit(
visitor{
[&](const BridgeType::BridgeVoidConverters& annotation) {
return llvm::json::Object{{
"BridgeVoidConverters",
llvm::json::Object{
{"rust_name", annotation.rust_name},
{"rust_to_cpp_converter", annotation.rust_to_cpp_converter},
{"cpp_to_rust_converter", annotation.cpp_to_rust_converter},
},
}};
},
[&](const BridgeType::Bridge& annotation) {
return llvm::json::Object{{
"Bridge",
llvm::json::Object{
{"rust_name", annotation.rust_name},
{"abi_rust", annotation.abi_rust},
{"abi_cpp", annotation.abi_cpp},
},
}};
},
[&](const BridgeType::StdOptional& std_optional) {
return llvm::json::Object{
{"StdOptional", std_optional.inner_type->ToJson()}};
},
[&](const BridgeType::StdPair& std_pair) {
return llvm::json::Object{
{"StdPair", llvm::json::Array{
std_pair.first_type->ToJson(),
std_pair.second_type->ToJson(),
}}};
},
[&](const BridgeType::StdString& std_string) {
return llvm::json::Object{{"StdString", nullptr}};
},
[&](const BridgeType::ProtoMessageBridge& proto_message_bridge) {
return llvm::json::Object{{
"ProtoMessageBridge",
llvm::json::Object{
{"rust_name", proto_message_bridge.rust_name},
{"abi_rust", proto_message_bridge.abi_rust},
{"abi_cpp", proto_message_bridge.abi_cpp},
},
}};
},
},
variant);
}
llvm::json::Value TemplateArg::ToJson() const {
return llvm::json::Object{
{"type", type},
};
}
llvm::json::Value TemplateSpecialization::ToJson() const {
return llvm::json::Object{
{"is_string_view", is_string_view},
{"is_wstring_view", is_wstring_view},
{"defining_target", defining_target},
{"template_name", template_name},
{"template_args", template_args},
};
}
TraitImplPolarity* absl_nullable TraitDerives::Polarity(
absl::string_view trait) {
// <internal link> start
if (trait == "Clone") return &clone;
if (trait == "Copy") return &copy;
if (trait == "Debug") return &debug;
// <internal link> end
return nullptr;
}
static std::string TraitImplPolarityToString(TraitImplPolarity polarity) {
switch (polarity) {
case TraitImplPolarity::kNegative:
return "Negative";
case TraitImplPolarity::kNone:
return "None";
case TraitImplPolarity::kPositive:
return "Positive";
}
}
llvm::json::Value TraitDerives::ToJson() const {
return llvm::json::Object{
// <internal link> start
{"clone", TraitImplPolarityToString(clone)},
{"copy", TraitImplPolarityToString(copy)},
{"debug", TraitImplPolarityToString(debug)},
// <internal link> end
{"send", send},
{"sync", sync},
{"custom", custom},
};
}
llvm::json::Value Record::ToJson() const {
std::vector<llvm::json::Value> json_item_ids;
json_item_ids.reserve(child_item_ids.size());
for (const auto& id : child_item_ids) {
json_item_ids.push_back(id.value());
}
llvm::json::Object record{
{"rs_name", rs_name},
{"cc_name", cc_name},
{"mangled_cc_name", mangled_cc_name},
{"id", id},
{"owning_target", owning_target},
{"template_specialization", template_specialization},
{"unknown_attr", unknown_attr},
{"doc_comment", doc_comment},
{"bridge_type", bridge_type},
{"source_loc", source_loc},
{"unambiguous_public_bases", unambiguous_public_bases},
{"fields", fields},
{"lifetime_params", lifetime_params},
{"size_align", size_align.ToJson()},
{"trait_derives", trait_derives.ToJson()},
{"is_derived_class", is_derived_class},
{"override_alignment", override_alignment},
{"is_unsafe_type", is_unsafe_type},
{"copy_constructor", copy_constructor},
{"move_constructor", move_constructor},
{"destructor", destructor},
{"is_trivial_abi", is_trivial_abi},
{"is_inheritable", is_inheritable},
{"is_abstract", is_abstract},
{"nodiscard", nodiscard},
{"record_type", RecordTypeToString(record_type)},
{"is_aggregate", is_aggregate},
{"is_anon_record_with_typedef", is_anon_record_with_typedef},
{"child_item_ids", std::move(json_item_ids)},
{"enclosing_item_id", enclosing_item_id},
{"must_bind", must_bind},
};
return llvm::json::Object{
{"Record", std::move(record)},
};
}
llvm::json::Value Enumerator::ToJson() const {
return llvm::json::Object{
{"identifier", identifier},
{"value", value},
{"unknown_attr", unknown_attr},
};
}
llvm::json::Value Enum::ToJson() const {
llvm::json::Object enum_ir{
{"cc_name", cc_name},
{"rs_name", rs_name},
{"id", id},
{"owning_target", owning_target},
{"source_loc", source_loc},
{"underlying_type", underlying_type},
{"enumerators", enumerators},
{"unknown_attr", unknown_attr},
{"enclosing_item_id", enclosing_item_id},
{"must_bind", must_bind},
};
return llvm::json::Object{
{"Enum", std::move(enum_ir)},
};
}
llvm::json::Value GlobalVar::ToJson() const {
llvm::json::Object var{
{"cc_name", cc_name},
{"rs_name", rs_name},
{"id", id},
{"owning_target", owning_target},
{"source_loc", source_loc},
{"mangled_name", mangled_name},
{"type", type},
{"unknown_attr", unknown_attr},
{"enclosing_item_id", enclosing_item_id},
{"must_bind", must_bind},
};
return llvm::json::Object{
{"GlobalVar", std::move(var)},
};
}
llvm::json::Value TypeAlias::ToJson() const {
llvm::json::Object type_alias{{"cc_name", cc_name},
{"rs_name", rs_name},
{"id", id},
{"owning_target", owning_target},
{"unknown_attr", unknown_attr},
{"doc_comment", doc_comment},
{"underlying_type", underlying_type},
{"source_loc", source_loc},
{"enclosing_item_id", enclosing_item_id},
{"must_bind", must_bind}};
return llvm::json::Object{
{"TypeAlias", std::move(type_alias)},
};
}
FormattedError FormattedError::FromStatus(absl::Status status) {
std::optional<absl::Cord> fmt_cord =
status.GetPayload(FormattedError::kFmtPayloadTypeUrl);
std::string fmt;
if (fmt_cord) {
fmt = std::string(*fmt_cord);
} else {
fmt = absl::StrCat("(unannotated `",
absl::StatusCodeToString(status.code()), "` status)");
}
return FormattedError(fmt, std::string(status.message()));
}
llvm::json::Value FormattedError::ToJson() const {
return llvm::json::Object{
{"fmt", fmt_},
{"message", message_},
};
}
static std::string UnsupportedItemKindToString(UnsupportedItem::Kind kind) {
switch (kind) {
case UnsupportedItem::Kind::kFunc:
return "Func";
case UnsupportedItem::Kind::kGlobalVar:
return "GlobalVar";
case UnsupportedItem::Kind::kStruct:
return "Struct";
case UnsupportedItem::Kind::kUnion:
return "Union";
case UnsupportedItem::Kind::kClass:
return "Class";
case UnsupportedItem::Kind::kEnum:
return "Enum";
case UnsupportedItem::Kind::kTypeAlias:
return "TypeAlias";
case UnsupportedItem::Kind::kNamespace:
return "Namespace";
case UnsupportedItem::Kind::kConstructor:
return "Constructor";
case UnsupportedItem::Kind::kOther:
return "Other";
}
}
llvm::json::Value UnsupportedItem::Path::ToJson() const {
return llvm::json::Object{
{"ident", ident},
{"enclosing_item_id", enclosing_item_id},
};
}
llvm::json::Value UnsupportedItem::ToJson() const {
std::vector<llvm::json::Value> json_errors;
json_errors.reserve(errors.size());
for (const auto& error : errors) {
json_errors.push_back(error.ToJson());
}
llvm::json::Object unsupported{
{"name", name},
{"kind", UnsupportedItemKindToString(kind)},
{"path", path},
{"errors", json_errors},
{"source_loc", source_loc},
{"id", id},
{"must_bind", must_bind},
};
return llvm::json::Object{
{"UnsupportedItem", std::move(unsupported)},
};
}
llvm::json::Value Comment::ToJson() const {
llvm::json::Object comment{
{"text", text},
{"id", id},
{"must_bind", must_bind},
};
comment["id"] = id.value();
return llvm::json::Object{
{"Comment", std::move(comment)},
};
}
llvm::json::Value Namespace::ToJson() const {
std::vector<llvm::json::Value> json_item_ids;
json_item_ids.reserve(child_item_ids.size());
for (const auto& id : child_item_ids) {
json_item_ids.push_back(id.value());
}
llvm::json::Object ns{
{"cc_name", cc_name},
{"rs_name", rs_name},
{"id", id},
{"canonical_namespace_id", canonical_namespace_id},
{"unknown_attr", unknown_attr},
{"owning_target", owning_target},
{"child_item_ids", std::move(json_item_ids)},
{"enclosing_item_id", enclosing_item_id},
{"is_inline", is_inline},
{"must_bind", must_bind},
};
return llvm::json::Object{
{"Namespace", std::move(ns)},
};
}
llvm::json::Value IR::ToJson() const {
std::vector<llvm::json::Value> json_items;
json_items.reserve(items.size());
for (const auto& item : items) {
std::visit([&](auto&& item) { json_items.push_back(item.ToJson()); }, item);
}
CHECK_EQ(json_items.size(), items.size());
llvm::json::Object top_level_item_ids_json;
for (const auto& [target, item_ids] : top_level_item_ids) {
std::vector<llvm::json::Value> item_ids_json;
item_ids_json.reserve(item_ids.size());
for (const auto& item_id : item_ids) {
item_ids_json.push_back(item_id.value());
}
top_level_item_ids_json[target.value()] = std::move(item_ids_json);
}
llvm::json::Object features_json;
for (const auto& [target, features] : crubit_features) {
std::vector<llvm::json::Value> feature_array;
for (const std::string& feature : features) {
feature_array.push_back(feature);
}
features_json[target.value()] = std::move(feature_array);
}
llvm::json::Object result{
{"public_headers", public_headers},
{"current_target", current_target},
{"items", std::move(json_items)},
{"top_level_item_ids", std::move(top_level_item_ids_json)},
{"crubit_features", std::move(features_json)},
};
if (!crate_root_path.empty()) {
result["crate_root_path"] = crate_root_path;
}
return std::move(result);
}
std::string ItemToString(const IR::Item& item) {
return std::visit(
[&](auto&& item) { return llvm::formatv("{0}", item.ToJson()); }, item);
}
void SetMustBindItem(IR::Item& item) {
// All IR::Item variants have a `must_bind` field.
std::visit([](auto& item_variant) { item_variant.must_bind = true; }, item);
}
} // namespace crubit