Split `Type` in half: separate `RsType` and `CcType`.
This is, I think, adequately motivated by the sheer impossibility of the original design working. Consider the following compound data type:
`int*` in C++ may be `Option<&i32>` in Rust.
This cannot be expressed in the old `Type` struct because it assumed the generic layout was the same: it assumed that if the C++ type was e.g. `*<int>`, then the rust type would be shaped like `a<b>` for some type names `a` and `b`. But in this case, `*<int>` becomes `Option<&<i32>>`. And anyway, `&` takes a lifetime parameter, so this is actually `Option<&<'_, i32>>`. Just not shaped the same at all!
There are many more cases beside. Here's an incomplete list of possible type maps which are incompatible with the old layout:
* `const char*` becomes `CStr`
* function pointers AND functions -- that is, `Identity<void()>` and `Identity<void()>*` -- both can become non-pointer functions in Rust: `extern "C" fn()->()`.
* `std::span<char, std::dynamic_extent>` (2 params) becomes `&mut [u8]` (1 param)
We should change this to do the right thing now, early on, rather than later when it will be even more annoying. :)
This is, in cool news, our first major divergence from [Clif's AST proto](http://google3/third_party/clif/protos/ast.proto;l=156-171;rcl=397117954).
PiperOrigin-RevId: 399622062
diff --git a/rs_bindings_from_cc/ast_visitor.cc b/rs_bindings_from_cc/ast_visitor.cc
index 71b45e5..0659bd8 100644
--- a/rs_bindings_from_cc/ast_visitor.cc
+++ b/rs_bindings_from_cc/ast_visitor.cc
@@ -123,40 +123,40 @@
return true;
}
-absl::StatusOr<Type> AstVisitor::ConvertType(
+absl::StatusOr<MappedType> AstVisitor::ConvertType(
clang::QualType qual_type, const clang::ASTContext& ctx) const {
- std::optional<Type> type = std::nullopt;
+ std::optional<MappedType> type = std::nullopt;
std::string type_string = qual_type.getAsString();
if (const clang::PointerType* pointer_type =
qual_type->getAs<clang::PointerType>()) {
auto pointee_type = ConvertType(pointer_type->getPointeeType(), ctx);
if (pointee_type.ok()) {
- type = Type::PointerTo(*pointee_type);
+ type = MappedType::PointerTo(*pointee_type);
}
} else if (const clang::BuiltinType* builtin_type =
qual_type->getAs<clang::BuiltinType>()) {
switch (builtin_type->getKind()) {
case clang::BuiltinType::Bool:
- type = {"bool", "bool"};
+ type = MappedType::Simple("bool", "bool");
break;
case clang::BuiltinType::Float:
- type = {"float", "float"};
+ type = MappedType::Simple("float", "float");
break;
case clang::BuiltinType::Double:
- type = {"double", "double"};
+ type = MappedType::Simple("double", "double");
break;
case clang::BuiltinType::Void:
- type = Type::Void();
+ type = MappedType::Void();
break;
default:
if (builtin_type->isIntegerType()) {
auto size = ctx.getTypeSize(builtin_type);
if (size == 8 || size == 16 || size == 32 || size == 64) {
- type = Type{
+ type = MappedType::Simple(
absl::Substitute(
"$0$1", builtin_type->isSignedInteger() ? 'i' : 'u', size),
- type_string};
+ type_string);
}
}
}
@@ -170,7 +170,7 @@
}
// Add cv-qualification.
- type->cc_const = qual_type.isConstQualified();
+ type->cc_type.is_const = qual_type.isConstQualified();
// Not doing volatile for now -- note that volatile pointers do not exist in
// Rust, though volatile reads/writes still do.
diff --git a/rs_bindings_from_cc/ast_visitor.h b/rs_bindings_from_cc/ast_visitor.h
index e08d504..b7a1fba 100644
--- a/rs_bindings_from_cc/ast_visitor.h
+++ b/rs_bindings_from_cc/ast_visitor.h
@@ -44,8 +44,8 @@
private:
std::string GetMangledName(const clang::NamedDecl* named_decl) const;
Identifier GetTranslatedName(const clang::NamedDecl* named_decl) const;
- absl::StatusOr<Type> ConvertType(clang::QualType qual_type,
- const clang::ASTContext& ctx) const;
+ absl::StatusOr<MappedType> ConvertType(clang::QualType qual_type,
+ const clang::ASTContext& ctx) const;
absl::Span<const absl::string_view> public_header_names_;
IR& ir_;
diff --git a/rs_bindings_from_cc/ast_visitor_test.cc b/rs_bindings_from_cc/ast_visitor_test.cc
index 3d4210e..fda3f9c 100644
--- a/rs_bindings_from_cc/ast_visitor_test.cc
+++ b/rs_bindings_from_cc/ast_visitor_test.cc
@@ -65,40 +65,70 @@
return testing::Field(&FuncParam::type, AllOf(matchers...));
}
-// Matches a Type that has the given Rust name.
-MATCHER_P(RsNameIs, rs_name, "") {
- if (arg.rs_name == rs_name) return true;
+// Matches a RsType or CcType that has the given name.
+MATCHER_P(NameIs, name, "") {
+ if (arg.name == name) return true;
- *result_listener << "actual rs_name: '" << arg.rs_name << "'";
+ *result_listener << "actual name: '" << arg.name << "'";
return false;
}
-// Matches a Type that has the given C++ name.
-MATCHER_P(CcNameIs, cc_name, "") {
- if (arg.cc_name == cc_name) return true;
-
- *result_listener << "actual cc_name: '" << arg.cc_name << "'";
- return false;
-}
-
-// Matches a type that has type parameters matching `matchers`.
+// Matches a MappedType with a CcType that matches all given matchers.
template <typename... Args>
-auto TypeParamsAre(const Args&... matchers) {
- return testing::Field(&Type::type_params, ElementsAre(matchers...));
+auto CcTypeIs(const Args&... matchers) {
+ return testing::Field(&MappedType::cc_type, AllOf(matchers...));
}
-// Matches a type that is void.
+// Matches a MappedType with a RsType that matches all given matchers.
+template <typename... Args>
+auto RsTypeIs(const Args&... matchers) {
+ return testing::Field(&MappedType::rs_type, AllOf(matchers...));
+}
+
+// Matches an RsType that has Rust type parameters matching `matchers`.
+template <typename... Args>
+auto RsTypeParamsAre(const Args&... matchers) {
+ return testing::Field(&RsType::type_params, ElementsAre(matchers...));
+}
+
+// Matches a CcType that has Rust type parameters matching `matchers`.
+template <typename... Args>
+auto CcTypeParamsAre(const Args&... matchers) {
+ return testing::Field(&CcType::type_params, ElementsAre(matchers...));
+}
+
+auto IsCcInt() { return AllOf(NameIs("int"), CcTypeParamsAre()); }
+
+auto IsRsInt() { return AllOf(NameIs("i32"), RsTypeParamsAre()); }
+
+// Matches a CcType that is a pointer to a type matching `matcher`.
+template <typename Matcher>
+auto CcPointsTo(const Matcher& matcher) {
+ return AllOf(NameIs("*"), CcTypeParamsAre(matcher));
+}
+
+// Matches an RsType that is a pointer to a type matching `matcher`.
+template <typename Matcher>
+auto RsPointsTo(const Matcher& matcher) {
+ return AllOf(NameIs("*mut"), RsTypeParamsAre(matcher));
+}
+
+// Matches a MappedType that is void.
MATCHER(IsVoid, "") { return arg.IsVoid(); }
-// Matches an integer type.
-auto IsInt() {
- return AllOf(RsNameIs("i32"), CcNameIs("int"), TypeParamsAre());
+// Matches a MappedType that is an integer.
+auto IsInt() { return AllOf(CcTypeIs(IsCcInt()), RsTypeIs(IsRsInt())); }
+
+// Matches a MappedType that is a pointer to integer.
+auto IsIntPtr() {
+ return AllOf(CcTypeIs(CcPointsTo(IsCcInt())),
+ RsTypeIs(RsPointsTo(IsRsInt())));
}
-// Matches a type that is a pointer to a type matching `matcher`.
-template <typename Matcher>
-auto PointsTo(const Matcher& matcher) {
- return AllOf(RsNameIs("*mut"), CcNameIs("*"), TypeParamsAre(matcher));
+// Matches a MappedType for cc and rs types with no type parameters.
+auto IsSimpleType(absl::string_view rs_name, absl::string_view cc_name) {
+ return AllOf(CcTypeIs(NameIs(cc_name), CcTypeParamsAre()),
+ RsTypeIs(NameIs(rs_name), RsTypeParamsAre()));
}
// Matches a Record that has fields matching `matchers`.
@@ -242,8 +272,8 @@
IR ir = ImportCode({"int* Foo(int* a);"}, {});
EXPECT_THAT(ir.functions,
- ElementsAre(AllOf(ReturnType(PointsTo(IsInt())),
- ParamsAre(ParamType(PointsTo(IsInt()))))));
+ ElementsAre(AllOf(ReturnType(IsIntPtr()),
+ ParamsAre(ParamType(IsIntPtr())))));
}
TEST(AstVisitorTest, Struct) {
@@ -340,44 +370,44 @@
EXPECT_THAT(
ir.records,
ElementsAre(FieldsAre(
- FieldType(RsNameIs("bool"), CcNameIs("bool")),
+ FieldType(IsSimpleType("bool", "bool")),
- FieldType(RsNameIs("i8"), CcNameIs("char")),
- FieldType(RsNameIs("u8"), CcNameIs("unsigned char")),
- FieldType(RsNameIs("i8"), CcNameIs("signed char")),
- FieldType(RsNameIs("u16"), CcNameIs("char16_t")),
+ FieldType(IsSimpleType("i8", "char")),
+ FieldType(IsSimpleType("u8", "unsigned char")),
+ FieldType(IsSimpleType("i8", "signed char")),
+ FieldType(IsSimpleType("u16", "char16_t")),
// We cannot map C++ char32_t or wchar_t to Rust char,
// because Rust requires that chars are valid UTF scalar values.
- FieldType(RsNameIs("u32"), CcNameIs("char32_t")),
- FieldType(RsNameIs("i32"), CcNameIs("wchar_t")),
+ FieldType(IsSimpleType("u32", "char32_t")),
+ FieldType(IsSimpleType("i32", "wchar_t")),
- FieldType(RsNameIs("i16"), CcNameIs("short")),
- FieldType(RsNameIs("i32"), CcNameIs("int")),
- FieldType(RsNameIs("i64"), CcNameIs("long")),
- FieldType(RsNameIs("i64"), CcNameIs("long long")),
+ FieldType(IsSimpleType("i16", "short")),
+ FieldType(IsSimpleType("i32", "int")),
+ FieldType(IsSimpleType("i64", "long")),
+ FieldType(IsSimpleType("i64", "long long")),
- FieldType(RsNameIs("u16"), CcNameIs("unsigned short")),
- FieldType(RsNameIs("u32"), CcNameIs("unsigned int")),
- FieldType(RsNameIs("u64"), CcNameIs("unsigned long")),
- FieldType(RsNameIs("u64"), CcNameIs("unsigned long long")),
+ FieldType(IsSimpleType("u16", "unsigned short")),
+ FieldType(IsSimpleType("u32", "unsigned int")),
+ FieldType(IsSimpleType("u64", "unsigned long")),
+ FieldType(IsSimpleType("u64", "unsigned long long")),
- FieldType(RsNameIs("i16"), CcNameIs("short")),
- FieldType(RsNameIs("i32"), CcNameIs("int")),
- FieldType(RsNameIs("i64"), CcNameIs("long")),
- FieldType(RsNameIs("i64"), CcNameIs("long long")),
+ FieldType(IsSimpleType("i16", "short")),
+ FieldType(IsSimpleType("i32", "int")),
+ FieldType(IsSimpleType("i64", "long")),
+ FieldType(IsSimpleType("i64", "long long")),
- FieldType(RsNameIs("i8"), CcNameIs("int8_t")),
- FieldType(RsNameIs("i16"), CcNameIs("int16_t")),
- FieldType(RsNameIs("i32"), CcNameIs("int32_t")),
- FieldType(RsNameIs("i64"), CcNameIs("int64_t")),
+ FieldType(IsSimpleType("i8", "int8_t")),
+ FieldType(IsSimpleType("i16", "int16_t")),
+ FieldType(IsSimpleType("i32", "int32_t")),
+ FieldType(IsSimpleType("i64", "int64_t")),
- FieldType(RsNameIs("u8"), CcNameIs("uint8_t")),
- FieldType(RsNameIs("u16"), CcNameIs("uint16_t")),
- FieldType(RsNameIs("u32"), CcNameIs("uint32_t")),
- FieldType(RsNameIs("u64"), CcNameIs("uint64_t")),
+ FieldType(IsSimpleType("u8", "uint8_t")),
+ FieldType(IsSimpleType("u16", "uint16_t")),
+ FieldType(IsSimpleType("u32", "uint32_t")),
+ FieldType(IsSimpleType("u64", "uint64_t")),
- FieldType(RsNameIs("float"), CcNameIs("float")),
- FieldType(RsNameIs("double"), CcNameIs("double")))));
+ FieldType(IsSimpleType("float", "float")),
+ FieldType(IsSimpleType("double", "double")))));
}
} // namespace
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index f1c4a97..92c5f4f 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -17,22 +17,44 @@
return result;
}
-nlohmann::json Type::ToJson() const {
+nlohmann::json RsType::ToJson() const {
nlohmann::json result;
std::vector<nlohmann::json> json_params;
json_params.reserve(type_params.size());
- for (const Type& param : type_params) {
+ for (const RsType& param : type_params) {
json_params.push_back(param.ToJson());
}
- result["rs_name"] = rs_name;
- result["cc_name"] = cc_name;
- result["cc_const"] = cc_const;
+ result["name"] = name;
result["type_params"] = std::move(json_params);
return result;
}
+nlohmann::json CcType::ToJson() const {
+ nlohmann::json result;
+
+ std::vector<nlohmann::json> json_params;
+ json_params.reserve(type_params.size());
+ for (const CcType& param : type_params) {
+ json_params.push_back(param.ToJson());
+ }
+ result["name"] = name;
+ result["is_const"] = is_const;
+ result["type_params"] = std::move(json_params);
+
+ return result;
+}
+
+nlohmann::json MappedType::ToJson() const {
+ nlohmann::json result;
+
+ result["rs_type"] = rs_type.ToJson();
+ result["cc_type"] = cc_type.ToJson();
+
+ return result;
+}
+
nlohmann::json Identifier::ToJson() const {
nlohmann::json result;
result["identifier"] = identifier_;
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index a87be71..a670deb 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -43,33 +43,12 @@
std::string name_;
};
-// A type involved in the bindings. It has the knowledge about how the type is
-// spelled in Rust and in C++ code.
-//
-// Examples:
-// C++'s `int32_t` will be `Type{"i32", "int"}`.
-// C++'s `struct Foo` will be `Type{"Foo", "Foo"}`.
-// C++'s `int*` will be `Type{"*mut", "*", {Type{"i32", "int"}}}
-struct Type {
- static Type Void() { return Type{"()", "void"}; }
- bool IsVoid() const { return rs_name == "()"; }
-
- static Type PointerTo(Type pointee_type) {
- absl::string_view rs_name =
- pointee_type.cc_const ? internal::kRustPtrConst : internal::kRustPtrMut;
- auto pointer_type = Type{.rs_name = std::string(rs_name),
- .cc_name = std::string(internal::kCcPtr)};
- pointer_type.type_params.push_back(std::move(pointee_type));
- return pointer_type;
- }
-
+// 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 rust name of the type. For example, i32 or ().
- std::string rs_name;
-
- // The C++ name for the type. For example, int or void.
- std::string cc_name;
+ // The name of the type. For example, int or void.
+ std::string name;
// The C++ const-qualification for the type.
//
@@ -77,12 +56,62 @@
// 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 cc_const = false;
+ 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<Type> type_params = {};
+ 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 = {};
+};
+
+// 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;
};
// An identifier involved in bindings.
@@ -116,7 +145,7 @@
struct FuncParam {
nlohmann::json ToJson() const;
- Type type;
+ MappedType type;
Identifier identifier;
};
@@ -126,7 +155,7 @@
Identifier identifier;
std::string mangled_name;
- Type return_type;
+ MappedType return_type;
std::vector<FuncParam> params;
bool is_inline;
};
@@ -143,7 +172,7 @@
nlohmann::json ToJson() const;
Identifier identifier;
- Type type;
+ MappedType type;
AccessSpecifier access;
// Field offset in bits.
uint64_t offset;
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 8ee14f0..5e3334f 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -18,11 +18,22 @@
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
-pub struct IRType {
- pub rs_name: String,
- pub cc_name: String,
- pub cc_const: bool,
- pub type_params: Vec<IRType>,
+pub struct RsType {
+ pub name: String,
+ pub type_params: Vec<RsType>,
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+pub struct CcType {
+ pub name: String,
+ pub is_const: bool,
+ pub type_params: Vec<CcType>,
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
+pub struct MappedType {
+ pub rs_type: RsType,
+ pub cc_type: CcType,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
@@ -33,7 +44,7 @@
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
pub struct FuncParam {
#[serde(rename(deserialize = "type"))]
- pub type_: IRType,
+ pub type_: MappedType,
pub identifier: Identifier,
}
@@ -41,7 +52,7 @@
pub struct Func {
pub identifier: Identifier,
pub mangled_name: String,
- pub return_type: IRType,
+ pub return_type: MappedType,
pub params: Vec<FuncParam>,
pub is_inline: bool,
}
@@ -57,7 +68,7 @@
pub struct Field {
pub identifier: Identifier,
#[serde(rename(deserialize = "type"))]
- pub type_: IRType,
+ pub type_: MappedType,
pub access: AccessSpecifier,
pub offset: i64,
}
@@ -110,10 +121,16 @@
"params": [
{
"identifier": { "identifier": "arg" },
- "type": { "rs_name":"i32", "cc_name": "int", "cc_const": false, "type_params": [] }
+ "type": {
+ "rs_type": {"name": "i32", "type_params": []},
+ "cc_type": {"name": "int", "is_const": false, "type_params": []}
+ }
}
],
- "return_type": { "rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
+ "return_type": {
+ "rs_type": {"name": "i32", "type_params": []},
+ "cc_type": {"name": "int", "is_const": false, "type_params": []}
+ },
"is_inline": false
}
]
@@ -124,18 +141,22 @@
functions: vec![Func {
identifier: Identifier { identifier: "hello_world".to_string() },
mangled_name: "$$mangled_name$$".to_string(),
- return_type: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ return_type: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
params: vec![FuncParam {
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
identifier: Identifier { identifier: "arg".to_string() },
}],
@@ -156,19 +177,28 @@
"fields": [
{
"identifier": {"identifier": "public_int" },
- "type": {"rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
+ "type": {
+ "rs_type": {"name": "i32", "type_params": []},
+ "cc_type": {"name": "int", "is_const": false, "type_params": []}
+ },
"access": "Public",
"offset": 0
},
{
"identifier": {"identifier": "protected_int" },
- "type": {"rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
+ "type": {
+ "rs_type": {"name": "i32", "type_params": []},
+ "cc_type": {"name": "int", "is_const": false, "type_params": []}
+ },
"access": "Protected",
"offset": 32
},
{
"identifier": {"identifier": "private_int" },
- "type": {"rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
+ "type": {
+ "rs_type": {"name": "i32", "type_params": []},
+ "cc_type": {"name": "int", "is_const": false, "type_params": []}
+ },
"access": "Private",
"offset": 64
}
@@ -186,33 +216,39 @@
fields: vec![
Field {
identifier: Identifier { identifier: "public_int".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
access: AccessSpecifier::Public,
offset: 0,
},
Field {
identifier: Identifier { identifier: "protected_int".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
access: AccessSpecifier::Protected,
offset: 32,
},
Field {
identifier: Identifier { identifier: "private_int".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
access: AccessSpecifier::Private,
offset: 64,
@@ -236,9 +272,14 @@
"fields": [
{
"identifier": {"identifier": "ptr" },
- "type": {"rs_name": "*mut", "cc_name": "*", "cc_const": false, "type_params": [
- {"rs_name": "SomeStruct", "cc_name": "SomeStruct", "cc_const": false, "type_params": []}
- ] },
+ "type": {
+ "rs_type": {"name": "*mut", "type_params": [
+ {"name": "SomeStruct", "type_params": []}
+ ]},
+ "cc_type": { "name": "*", "is_const": false, "type_params": [
+ {"name": "SomeStruct", "is_const": false, "type_params": []}
+ ]}
+ },
"access": "Public",
"offset": 0
}
@@ -255,16 +296,23 @@
identifier: Identifier { identifier: "SomeStruct".to_string() },
fields: vec![Field {
identifier: Identifier { identifier: "ptr".to_string() },
- type_: IRType {
- rs_name: "*mut".to_string(),
- cc_name: "*".to_string(),
- cc_const: false,
- type_params: vec![IRType {
- rs_name: "SomeStruct".to_string(),
- cc_name: "SomeStruct".to_string(),
- cc_const: false,
- type_params: vec![],
- }],
+ type_: MappedType {
+ rs_type: RsType {
+ name: "*mut".to_string(),
+ type_params: vec![RsType {
+ name: "SomeStruct".to_string(),
+ type_params: vec![],
+ }],
+ },
+ cc_type: CcType {
+ name: "*".to_string(),
+ is_const: false,
+ type_params: vec![CcType {
+ name: "SomeStruct".to_string(),
+ is_const: false,
+ type_params: vec![],
+ }],
+ },
},
access: AccessSpecifier::Public,
offset: 0,
diff --git a/rs_bindings_from_cc/ir_test.cc b/rs_bindings_from_cc/ir_test.cc
index aa8a255..025357f 100644
--- a/rs_bindings_from_cc/ir_test.cc
+++ b/rs_bindings_from_cc/ir_test.cc
@@ -15,16 +15,22 @@
TEST(IrTest, TypeToJson) {
nlohmann::json expected = nlohmann::json::parse(R"j({
- "rs_name": "CompoundRs",
- "cc_name": "CompoundCc",
- "cc_const": false,
- "type_params": [
- { "rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": []}
- ]
+ "rs_type": {
+ "name": "CompoundRs",
+ "type_params": [{"name": "i32", "type_params": []}]
+ },
+ "cc_type": {
+ "name": "CompoundCc",
+ "is_const": false,
+ "type_params": [
+ {"is_const": false, "name": "int", "type_params": []}
+ ]
+ }
})j");
- auto type = Type{.rs_name = "CompoundRs",
- .cc_name = "CompoundCc",
- .type_params = {Type{"i32", "int"}}};
+ auto type = MappedType{.rs_type = RsType{"CompoundRs", {RsType{"i32"}}},
+ .cc_type = CcType{.name = "CompoundCc",
+ .is_const = false,
+ .type_params = {CcType{"int"}}}};
EXPECT_EQ(type.ToJson(), expected);
}
@@ -33,36 +39,69 @@
R"j({
"used_headers": [{ "name": "foo/bar.h" }],
"functions": [{
- "identifier": { "identifier": "hello_world" },
- "mangled_name": "#$mangled_name$#",
- "return_type": { "rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
- "params": [
- {
- "identifier": {"identifier": "arg" },
- "type": { "rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] }
- }
- ],
- "is_inline": false
+ "identifier": { "identifier": "hello_world" },
+ "mangled_name": "#$mangled_name$#",
+ "return_type": {
+ "rs_type": { "name": "i32", "type_params": [] },
+ "cc_type": {
+ "is_const": false,
+ "name": "int",
+ "type_params": []
+ }
+ },
+ "params": [{
+ "identifier": { "identifier": "arg" },
+ "type": {
+ "rs_type": { "name": "i32", "type_params": [] },
+ "cc_type": {
+ "is_const": false,
+ "name": "int",
+ "type_params": []
+ }
+ }
+ }],
+ "is_inline": false
}],
"records": [
{
- "identifier": {"identifier": "SomeStruct" },
+ "identifier": { "identifier": "SomeStruct" },
"fields": [
{
- "identifier": {"identifier": "public_int" },
- "type": {"rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
+ "identifier": { "identifier": "public_int" },
+ "type": {
+ "rs_type": { "name": "i32", "type_params": [] },
+ "cc_type": {
+ "is_const": false,
+ "name": "int",
+ "type_params": []
+ }
+ },
"access": "Public",
"offset": 0
},
{
- "identifier": {"identifier": "protected_int" },
- "type": {"rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
+ "identifier": { "identifier": "protected_int" },
+ "type": {
+ "rs_type": { "name": "i32", "type_params": [] },
+ "cc_type": {
+ "is_const": false,
+ "name": "int",
+ "type_params": []
+ }
+ },
"access": "Protected",
"offset": 32
},
{
- "identifier": {"identifier": "private_int" },
- "type": {"rs_name": "i32", "cc_name": "int", "cc_const": false, "type_params": [] },
+ "identifier": { "identifier": "private_int" },
+ "type": {
+ "rs_type": { "name": "i32", "type_params": [] },
+ "cc_type": {
+ "is_const": false,
+ "name": "int",
+ "type_params": []
+ }
+ },
"access": "Private",
"offset": 64
}
@@ -71,38 +110,34 @@
"alignment": 4
}
]
- })j");
- IR
- ir =
- {
- .used_headers = {HeaderName("foo/bar.h")},
- .functions = {Func{
- .identifier = Identifier("hello_world"),
- .mangled_name = "#$mangled_name$#",
- .return_type = Type{"i32", "int"},
- .params = {FuncParam{Type{"i32", "int"}, Identifier("arg")}},
- .is_inline = false}},
- .records = {Record{.identifier = Identifier("SomeStruct"),
- .fields =
- {
- Field{.identifier =
- Identifier("public_int"),
- .type = Type{"i32", "int"},
- .access = kPublic,
- .offset = 0},
- Field{.identifier = Identifier(
- "protected_int"),
- .type = Type{"i32", "int"},
- .access = kProtected,
- .offset = 32},
- Field{.identifier = Identifier(
- "private_int"),
- .type = Type{"i32", "int"},
- .access = kPrivate,
- .offset = 64},
- },
- .size = 12,
- .alignment = 4}}};
+ })j");
+ IR ir = {
+ .used_headers = {HeaderName("foo/bar.h")},
+ .functions = {Func{.identifier = Identifier("hello_world"),
+ .mangled_name = "#$mangled_name$#",
+ .return_type = MappedType::Simple("i32", "int"),
+ .params = {FuncParam{MappedType::Simple("i32", "int"),
+ Identifier("arg")}},
+ .is_inline = false}},
+ .records = {Record{.identifier = Identifier("SomeStruct"),
+ .fields =
+ {
+ Field{.identifier = Identifier("public_int"),
+ .type = MappedType::Simple("i32", "int"),
+ .access = kPublic,
+ .offset = 0},
+ Field{
+ .identifier = Identifier("protected_int"),
+ .type = MappedType::Simple("i32", "int"),
+ .access = kProtected,
+ .offset = 32},
+ Field{.identifier = Identifier("private_int"),
+ .type = MappedType::Simple("i32", "int"),
+ .access = kPrivate,
+ .offset = 64},
+ },
+ .size = 12,
+ .alignment = 4}}};
EXPECT_EQ(ir.ToJson(), expected);
}
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 20d9d14..b1ea084 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -86,8 +86,11 @@
let ident = make_ident(&record.identifier.identifier);
let field_idents =
record.fields.iter().map(|f| make_ident(&f.identifier.identifier)).collect_vec();
- let field_types =
- record.fields.iter().map(|f| format_rs_type(&f.type_)).collect::<Result<Vec<_>>>()?;
+ let field_types = record
+ .fields
+ .iter()
+ .map(|f| format_rs_type(&f.type_.rs_type))
+ .collect::<Result<Vec<_>>>()?;
let field_accesses = record
.fields
.iter()
@@ -120,13 +123,16 @@
let ident = make_ident(&func.identifier.identifier);
let thunk_ident = format_ident!("__rust_thunk__{}", &func.identifier.identifier);
// TODO(hlopko): do not emit `-> ()` when return type is void, it's implicit.
- let return_type_name = format_rs_type(&func.return_type)?;
+ let return_type_name = format_rs_type(&func.return_type.rs_type)?;
let param_idents =
func.params.iter().map(|p| make_ident(&p.identifier.identifier)).collect_vec();
- let param_types =
- func.params.iter().map(|p| format_rs_type(&p.type_)).collect::<Result<Vec<_>>>()?;
+ let param_types = func
+ .params
+ .iter()
+ .map(|p| format_rs_type(&p.type_.rs_type))
+ .collect::<Result<Vec<_>>>()?;
api_funcs.push(quote! {
#[inline(always)]
@@ -185,8 +191,8 @@
format_ident!("{}", ident)
}
-fn format_rs_type(ty: &ir::IRType) -> Result<TokenStream> {
- let ptr_fragment = match ty.rs_name.as_str() {
+fn format_rs_type(ty: &ir::RsType) -> Result<TokenStream> {
+ let ptr_fragment = match ty.name.as_str() {
"*mut" => Some(quote! {*mut}),
"*const" => Some(quote! {*const}),
_ => None,
@@ -206,19 +212,19 @@
if ty.type_params.len() > 0 {
return Err(anyhow!("Type not yet supported: {:?}", ty));
}
- let ident = make_ident(&ty.rs_name);
+ let ident = make_ident(&ty.name);
Ok(quote! {#ident})
}
}
}
-fn format_cc_type(ty: &ir::IRType) -> Result<TokenStream> {
- let const_fragment = if ty.cc_const {
+fn format_cc_type(ty: &ir::CcType) -> Result<TokenStream> {
+ let const_fragment = if ty.is_const {
quote! {const}
} else {
quote! {}
};
- match ty.cc_name.as_str() {
+ match ty.name.as_str() {
"*" => {
if ty.type_params.len() != 1 {
return Err(anyhow!(
@@ -255,13 +261,16 @@
let thunk_ident = format_ident!("__rust_thunk__{}", &func.identifier.identifier);
let ident = make_ident(&func.identifier.identifier);
- let return_type_name = format_cc_type(&func.return_type)?;
+ let return_type_name = format_cc_type(&func.return_type.cc_type)?;
let param_idents =
func.params.iter().map(|p| make_ident(&p.identifier.identifier)).collect_vec();
- let param_types =
- func.params.iter().map(|p| format_cc_type(&p.type_)).collect::<Result<Vec<_>>>()?;
+ let param_types = func
+ .params
+ .iter()
+ .map(|p| format_cc_type(&p.type_.cc_type))
+ .collect::<Result<Vec<_>>>()?;
thunks.push(quote! {
extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
@@ -299,29 +308,35 @@
functions: vec![Func {
identifier: Identifier { identifier: "add".to_string() },
mangled_name: "_Z3Addii".to_string(),
- return_type: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ return_type: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
params: vec![
FuncParam {
identifier: Identifier { identifier: "a".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
},
FuncParam {
identifier: Identifier { identifier: "b".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
},
],
@@ -360,29 +375,35 @@
functions: vec![Func {
identifier: Identifier { identifier: "add".to_string() },
mangled_name: "_Z3Addii".to_string(),
- return_type: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ return_type: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
params: vec![
FuncParam {
identifier: Identifier { identifier: "a".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
},
FuncParam {
identifier: Identifier { identifier: "b".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
},
],
@@ -429,33 +450,39 @@
fields: vec![
Field {
identifier: Identifier { identifier: "public_int".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
access: AccessSpecifier::Public,
offset: 0,
},
Field {
identifier: Identifier { identifier: "protected_int".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
access: AccessSpecifier::Protected,
offset: 32,
},
Field {
identifier: Identifier { identifier: "private_int".to_string() },
- type_: IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType { name: "i32".to_string(), type_params: vec![] },
+ cc_type: CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ },
},
access: AccessSpecifier::Private,
offset: 64,
@@ -495,34 +522,47 @@
functions: vec![Func {
identifier: Identifier { identifier: "Deref".to_string() },
mangled_name: "_Z5DerefPKPi".to_string(),
- return_type: IRType {
- rs_name: "*mut".to_string(),
- cc_name: "*".to_string(),
- cc_const: false,
- type_params: vec![IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
- }],
+ return_type: MappedType {
+ rs_type: RsType {
+ name: "*mut".to_string(),
+ type_params: vec![RsType { name: "i32".to_string(), type_params: vec![] }],
+ },
+ cc_type: CcType {
+ name: "*".to_string(),
+ is_const: false,
+ type_params: vec![CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ }],
+ },
},
params: vec![FuncParam {
identifier: Identifier { identifier: "p".to_string() },
- type_: IRType {
- rs_name: "*const".to_string(),
- cc_name: "*".to_string(),
- cc_const: false,
- type_params: vec![IRType {
- rs_name: "*mut".to_string(),
- cc_name: "*".to_string(),
- cc_const: true,
- type_params: vec![IRType {
- rs_name: "i32".to_string(),
- cc_name: "int".to_string(),
- cc_const: false,
- type_params: vec![],
+ type_: MappedType {
+ rs_type: RsType {
+ name: "*const".to_string(),
+ type_params: vec![RsType {
+ name: "*mut".to_string(),
+ type_params: vec![RsType {
+ name: "i32".to_string(),
+ type_params: vec![],
+ }],
}],
- }],
+ },
+ cc_type: CcType {
+ name: "*".to_string(),
+ is_const: false,
+ type_params: vec![CcType {
+ name: "*".to_string(),
+ is_const: true,
+ type_params: vec![CcType {
+ name: "int".to_string(),
+ is_const: false,
+ type_params: vec![],
+ }],
+ }],
+ },
},
}],
is_inline: true,
diff --git a/rs_bindings_from_cc/src_code_gen_test.cc b/rs_bindings_from_cc/src_code_gen_test.cc
index cd782f2..ff4a8a2 100644
--- a/rs_bindings_from_cc/src_code_gen_test.cc
+++ b/rs_bindings_from_cc/src_code_gen_test.cc
@@ -17,14 +17,15 @@
using ::testing::StrEq;
TEST(SrcGenTest, FFIIntegration) {
- IR ir = {.used_headers = {HeaderName("foo/bar.h")},
- .functions = {Func{
- .identifier = Identifier("hello_world"),
- .mangled_name = "$$mangled_name$$",
- .return_type = Type{"i32", "int"},
- .params = {FuncParam{Type{"i32", "int"}, Identifier("arg")}},
- .is_inline = true}},
- .records = {}};
+ IR ir = {
+ .used_headers = {HeaderName("foo/bar.h")},
+ .functions = {Func{.identifier = Identifier("hello_world"),
+ .mangled_name = "$$mangled_name$$",
+ .return_type = MappedType::Simple("i32", "int"),
+ .params = {FuncParam{MappedType::Simple("i32", "int"),
+ Identifier("arg")}},
+ .is_inline = true}},
+ .records = {}};
Bindings bindings = GenerateBindings(ir);
EXPECT_THAT(
bindings.rs_api,