Splitting ImportType into ImportType and ImportQualType.
This is desirable because:
1. This helps propagate the error message from nested/recursive
ConvertType calls.
2. This helps use ASSIGN_OR_RETURN (which makes the code slightly more
readable).
PiperOrigin-RevId: 432939689
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index db0b7c1..655507a 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -48,6 +48,7 @@
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallPtrSet.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Casting.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Regex.h"
+#include "util/task/status_macros.h"
namespace rs_bindings_from_cc {
namespace {
@@ -520,8 +521,9 @@
this_lifetimes = lifetimes->this_lifetimes;
all_lifetimes.insert(this_lifetimes->begin(), this_lifetimes->end());
}
- auto param_type = ConvertType(method_decl->getThisType(), this_lifetimes,
- /*nullable=*/false);
+ auto param_type =
+ ConvertQualType(method_decl->getThisType(), this_lifetimes,
+ /*nullable=*/false);
if (!param_type.ok()) {
add_error(absl::StrCat("`this` parameter is not supported: ",
param_type.status().message()));
@@ -541,7 +543,7 @@
param_lifetimes = lifetimes->param_lifetimes[i];
all_lifetimes.insert(param_lifetimes->begin(), param_lifetimes->end());
}
- auto param_type = ConvertType(param->getType(), param_lifetimes);
+ auto param_type = ConvertQualType(param->getType(), param_lifetimes);
if (!param_type.ok()) {
add_error(absl::Substitute("Parameter #$0 is not supported: $1", i,
param_type.status().message()));
@@ -591,7 +593,7 @@
all_lifetimes.insert(return_lifetimes->begin(), return_lifetimes->end());
}
auto return_type =
- ConvertType(function_decl->getReturnType(), return_lifetimes);
+ ConvertQualType(function_decl->getReturnType(), return_lifetimes);
if (!return_type.ok()) {
add_error(absl::StrCat("Return type is not supported: ",
return_type.status().message()));
@@ -822,7 +824,7 @@
"Forward declared enums without type specifiers are not supported");
}
std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
- absl::StatusOr<MappedType> type = ConvertType(cc_type, no_lifetimes);
+ absl::StatusOr<MappedType> type = ConvertQualType(cc_type, no_lifetimes);
if (!type.ok()) {
return LookupResult(type.status().ToString());
}
@@ -879,7 +881,7 @@
}
std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
absl::StatusOr<MappedType> underlying_type =
- ConvertType(typedef_name_decl->getUnderlyingType(), no_lifetimes);
+ ConvertQualType(typedef_name_decl->getUnderlyingType(), no_lifetimes);
if (underlying_type.ok()) {
known_type_decls_.insert(typedef_name_decl);
return LookupResult(
@@ -938,19 +940,17 @@
}
absl::StatusOr<MappedType> Importer::ConvertType(
- clang::QualType qual_type,
+ const clang::Type* type,
std::optional<devtools_rust::TypeLifetimes>& lifetimes,
bool nullable) const {
- std::optional<MappedType> type = std::nullopt;
- // When converting the type to a string, don't include qualifiers -- we handle
- // these separately.
- std::string type_string = qual_type.getUnqualifiedType().getAsString();
+ // Qualifiers are handled separately in ConvertQualType().
+ std::string type_string = clang::QualType(type, 0).getAsString();
if (auto maybe_mapped_type = MapKnownCcTypeToRsType(type_string);
maybe_mapped_type.has_value()) {
- type = MappedType::Simple(std::string(*maybe_mapped_type), type_string);
- } else if (qual_type->isPointerType() || qual_type->isLValueReferenceType()) {
- clang::QualType pointee_type = qual_type->getPointeeType();
+ return MappedType::Simple(std::string(*maybe_mapped_type), type_string);
+ } else if (type->isPointerType() || type->isLValueReferenceType()) {
+ clang::QualType pointee_type = type->getPointeeType();
std::optional<LifetimeId> lifetime;
if (lifetimes.has_value()) {
CHECK(!lifetimes->empty());
@@ -966,100 +966,120 @@
"not supported: ",
type_string));
}
- do {
- clang::StringRef cc_call_conv =
- clang::FunctionType::getNameForCallConv(func_type->getCallConv());
- absl::StatusOr<absl::string_view> rs_abi =
- ConvertCcCallConvIntoRsAbi(func_type->getCallConv());
- if (!rs_abi.ok()) return rs_abi.status();
- auto return_type = ConvertType(func_type->getReturnType(), lifetimes);
- if (!return_type.ok()) break;
+ clang::StringRef cc_call_conv =
+ clang::FunctionType::getNameForCallConv(func_type->getCallConv());
+ ASSIGN_OR_RETURN(absl::string_view rs_abi,
+ ConvertCcCallConvIntoRsAbi(func_type->getCallConv()));
+ ASSIGN_OR_RETURN(MappedType mapped_return_type,
+ ConvertQualType(func_type->getReturnType(), lifetimes));
- std::vector<MappedType> param_types;
- for (const clang::QualType& param_type : func_type->getParamTypes()) {
- auto param_type_status = ConvertType(param_type, lifetimes);
- if (!param_type_status.ok()) break;
- param_types.push_back(*param_type_status);
- }
-
- if (qual_type->isPointerType()) {
- type = MappedType::FuncPtr(cc_call_conv, *rs_abi, lifetime,
- *return_type, param_types);
- } else {
- DCHECK(qual_type->isLValueReferenceType());
- type = MappedType::FuncRef(cc_call_conv, *rs_abi, lifetime,
- *return_type, param_types);
- }
- } while (false);
- } else {
- auto mapped_pointee_type = ConvertType(pointee_type, lifetimes);
- if (mapped_pointee_type.ok()) {
- if (qual_type->isPointerType()) {
- type =
- MappedType::PointerTo(*mapped_pointee_type, lifetime, nullable);
- } else {
- DCHECK(qual_type->isLValueReferenceType());
- type = MappedType::LValueReferenceTo(*mapped_pointee_type, lifetime);
- }
+ std::vector<MappedType> mapped_param_types;
+ for (const clang::QualType& param_type : func_type->getParamTypes()) {
+ ASSIGN_OR_RETURN(MappedType mapped_param_type,
+ ConvertQualType(param_type, lifetimes));
+ mapped_param_types.push_back(std::move(mapped_param_type));
}
+
+ if (type->isPointerType()) {
+ return MappedType::FuncPtr(cc_call_conv, rs_abi, lifetime,
+ std::move(mapped_return_type),
+ std::move(mapped_param_types));
+ } else {
+ DCHECK(type->isLValueReferenceType());
+ return MappedType::FuncRef(cc_call_conv, rs_abi, lifetime,
+ std::move(mapped_return_type),
+ std::move(mapped_param_types));
+ }
+ }
+
+ ASSIGN_OR_RETURN(MappedType mapped_pointee_type,
+ ConvertQualType(pointee_type, lifetimes));
+ if (type->isPointerType()) {
+ return MappedType::PointerTo(std::move(mapped_pointee_type), lifetime,
+ nullable);
+ } else {
+ DCHECK(type->isLValueReferenceType());
+ return MappedType::LValueReferenceTo(std::move(mapped_pointee_type),
+ lifetime);
}
} else if (const auto* builtin_type =
// Use getAsAdjusted instead of getAs so we don't desugar
// typedefs.
- qual_type->getAsAdjusted<clang::BuiltinType>()) {
+ type->getAsAdjusted<clang::BuiltinType>()) {
switch (builtin_type->getKind()) {
case clang::BuiltinType::Bool:
- type = MappedType::Simple("bool", "bool");
+ return MappedType::Simple("bool", "bool");
break;
case clang::BuiltinType::Float:
- type = MappedType::Simple("f32", "float");
+ return MappedType::Simple("f32", "float");
break;
case clang::BuiltinType::Double:
- type = MappedType::Simple("f64", "double");
+ return MappedType::Simple("f64", "double");
break;
case clang::BuiltinType::Void:
- type = MappedType::Void();
+ return MappedType::Void();
break;
default:
if (builtin_type->isIntegerType()) {
auto size = ctx_.getTypeSize(builtin_type);
if (size == 8 || size == 16 || size == 32 || size == 64) {
- type = MappedType::Simple(
+ return MappedType::Simple(
absl::Substitute(
"$0$1", builtin_type->isSignedInteger() ? 'i' : 'u', size),
type_string);
}
}
}
- } else if (const auto* tag_type =
- qual_type->getAsAdjusted<clang::TagType>()) {
+ } else if (const auto* tag_type = type->getAsAdjusted<clang::TagType>()) {
clang::TagDecl* tag_decl = tag_type->getDecl();
+ // TODO(lukasza): Rather than falling back to `absl::UnimplementedError` at
+ // the bottom of this method, we should explicitly emit an error when
+ // `tag_decl` is missing from `known_type_decls` or can't be handled by
+ // GetTranslatedIdentifier. See also a corresponding TODO in
+ // `test_record_with_unsupported_field`.
if (known_type_decls_.contains(tag_decl)) {
if (std::optional<Identifier> id = GetTranslatedIdentifier(tag_decl)) {
std::string ident(id->Ident());
DeclId decl_id = GenerateDeclId(tag_decl);
- type = MappedType::WithDeclIds(ident, decl_id, ident, decl_id);
+ return MappedType::WithDeclIds(ident, decl_id, ident, decl_id);
}
}
} else if (const auto* typedef_type =
- qual_type->getAsAdjusted<clang::TypedefType>()) {
+ type->getAsAdjusted<clang::TypedefType>()) {
clang::TypedefNameDecl* typedef_name_decl = typedef_type->getDecl();
+ // TODO(lukasza): Rather than falling back to `absl::UnimplementedError` at
+ // the bottom of this method, we should explicitly emit an error when
+ // `typedef_name_decl` is missing from `known_type_decls` or can't be
+ // handled by GetTranslatedIdentifier. See also a corresponding TODO in
+ // `test_record_with_unsupported_field`.
+ // TODO(lukasza): Consider merging with `TagType` handling above.
if (known_type_decls_.contains(typedef_name_decl)) {
if (std::optional<Identifier> id =
GetTranslatedIdentifier(typedef_name_decl)) {
std::string ident(id->Ident());
DeclId decl_id = GenerateDeclId(typedef_name_decl);
- type = MappedType::WithDeclIds(ident, decl_id, ident, decl_id);
+ return MappedType::WithDeclIds(ident, decl_id, ident, decl_id);
}
}
}
- if (!type.has_value()) {
- absl::Status error = absl::UnimplementedError(
- absl::Substitute("Unsupported type '$0'", type_string));
+ return absl::UnimplementedError(absl::StrCat(
+ "Unsupported clang::Type class '", type->getTypeClassName(), "'"));
+}
+
+absl::StatusOr<MappedType> Importer::ConvertQualType(
+ clang::QualType qual_type,
+ std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+ bool nullable) const {
+ std::string type_string = qual_type.getAsString();
+ absl::StatusOr<MappedType> type =
+ ConvertType(qual_type.getTypePtr(), lifetimes, nullable);
+ if (!type.ok()) {
+ absl::Status error = absl::UnimplementedError(absl::Substitute(
+ "Unsupported type '$0': $1", type_string, type.status().message()));
error.SetPayload(kTypeStatusPayloadUrl, absl::Cord(type_string));
return error;
}
@@ -1071,7 +1091,7 @@
absl::StrCat("Unsupported `volatile` qualifier: ", type_string));
}
- return *std::move(type);
+ return type;
}
absl::StatusOr<std::vector<Field>> Importer::ImportFields(
@@ -1082,7 +1102,7 @@
const clang::ASTRecordLayout& layout = ctx_.getASTRecordLayout(record_decl);
for (const clang::FieldDecl* field_decl : record_decl->fields()) {
std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
- auto type = ConvertType(field_decl->getType(), no_lifetimes);
+ auto type = ConvertQualType(field_decl->getType(), no_lifetimes);
if (!type.ok()) {
return absl::UnimplementedError(absl::Substitute(
"Type of field '$0' is not supported: $1",