Skip to content

Commit 42ba985

Browse files
authored
Selectively load rules in the same TU as the translated code (#451)
Part of #447. This only loads the rules into cpp2rust, AST matching comes in a future PR: > To implement this, rules are included in the translated TU. Only the rules that parse are kept. If the TU never included <map> then there are no rules for map. This is faster than the string matching approach. cpp_rule_indexer runs at build time to build an index of rules. This index is used by rule_loader to lazily load the rules into the converter for doing AST matching. There are 2 index files: one for C code and one for C++ code. Both live in build/rules_index. C translated code only loads the C index, C++ translated code loads both indexes. Example of index: ``` key: std/vector/push_back requires: std/vector dir: vector text: template <typename T1> void f14(std::vector<T1> &o, T1 &&value) { return o.push_back(std::move(value)); } key: std/basic_ifstream/rdbuf requires: std/basic_ifstream, std/basic_filebuf dir: ifstream text: std::filebuf *f4(const std::ifstream &o) { return o.rdbuf(); } ``` `key` is generated using the dependent name of the rules for templates and using the concrete name for non-templates. `text` is extracted from the file and appended into the translated TU if it needs the rule. `requires` only loads the rule in the TU if the translated code uses all the dependencies, basic_ifstream and basic_fielbuf in the example. This makes the translation fast. With 100K dummy rules, it takes on avergage 20ms per invocation to selectively load the rules. With full loading of all the 100K dummy rules, it takes 5 seconds on average.
1 parent 7f2e853 commit 42ba985

44 files changed

Lines changed: 1267 additions & 139 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CMakeLists.txt‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,17 @@ add_custom_command(
219219
)
220220

221221
file(GLOB rule_subdirs CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/rules/*)
222+
file(GLOB rule_common_headers CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/rules/common/*.h)
223+
file(GLOB rule_indexer_sources CONFIGURE_DEPENDS
224+
${PROJECT_SOURCE_DIR}/cpp2rust/rule_preprocessing/*
225+
${PROJECT_SOURCE_DIR}/cpp2rust/converter/rules/rules_loader.*)
226+
set(_common_headers)
227+
foreach(_header IN LISTS rule_common_headers)
228+
list(APPEND _common_headers --common-header=${_header})
229+
endforeach()
222230
set(cpp_rules_ir_outputs)
231+
set(cpp_rules_index_inputs)
232+
set(cpp_rules_index_outputs ${CMAKE_BINARY_DIR}/rules_index/c.json ${CMAKE_BINARY_DIR}/rules_index/cpp.json)
223233
set(rust_rules_ir_outputs)
224234
foreach(_rule_dir IN LISTS rule_subdirs)
225235
file(GLOB _cpp_srcs ${_rule_dir}/src.c ${_rule_dir}/src.cpp)
@@ -229,7 +239,7 @@ foreach(_rule_dir IN LISTS rule_subdirs)
229239
get_filename_component(_rule_name ${_rule_dir} NAME)
230240
set(_out_dir ${RULES_IR_DIR}/${_rule_name})
231241
set(_cpp_out ${_out_dir}/ir_src.json)
232-
set(_cxxflags --cxxflags=-isystem${_rule_dir})
242+
set(_cxxflags)
233243
set(_cxxflags_file)
234244
if(EXISTS ${_rule_dir}/cxxflags)
235245
set(_cxxflags_file ${_rule_dir}/cxxflags)
@@ -242,11 +252,15 @@ foreach(_rule_dir IN LISTS rule_subdirs)
242252
OUTPUT ${_cpp_out}
243253
COMMAND ${CMAKE_COMMAND} -E make_directory ${_out_dir}
244254
COMMAND $<TARGET_FILE:cpp-rule-preprocessor> --dir ${_rule_dir} --out ${_cpp_out} ${_cxxflags}
245-
DEPENDS ${_cpp_srcs} ${_cxxflags_file} ${PROJECT_SOURCE_DIR}/cpp2rust/cpp_rule_preprocessor.cpp ${RUSTLS_HEADER} ${BROTLI_HEADERS}
255+
DEPENDS ${_cpp_srcs} ${_cxxflags_file} ${rule_common_headers} ${PROJECT_SOURCE_DIR}/cpp2rust/rule_preprocessing/cpp_rule_preprocessor.cpp ${RUSTLS_HEADER} ${BROTLI_HEADERS}
246256
VERBATIM
247257
)
248258
list(APPEND cpp_rules_ir_outputs ${_cpp_out})
249259

260+
if(NOT _rule_name STREQUAL "builtin_types")
261+
list(APPEND cpp_rules_index_inputs ${_cpp_srcs} ${_cxxflags_file})
262+
endif()
263+
250264
set(_rust_srcs)
251265
set(_rust_out)
252266
if(EXISTS ${_rule_dir}/tgt_unsafe.rs)
@@ -277,6 +291,15 @@ endforeach()
277291

278292
add_custom_target("preprocess-cpp-rules" ALL DEPENDS ${cpp_rules_ir_outputs})
279293

294+
add_custom_command(
295+
OUTPUT ${cpp_rules_index_outputs}
296+
COMMAND $<TARGET_FILE:cpp-rule-indexer> --exclude builtin_types ${_common_headers}
297+
DEPENDS ${cpp_rules_index_inputs} ${rule_common_headers} ${rule_indexer_sources} ${RUSTLS_HEADER} ${BROTLI_HEADERS}
298+
VERBATIM
299+
)
300+
301+
add_custom_target("index-cpp-rules" ALL DEPENDS ${cpp_rules_index_outputs})
302+
280303
add_custom_target("preprocess-rust-rules" ALL
281304
DEPENDS ${rust_rules_ir_outputs})
282305

‎cpp2rust/CMakeLists.txt‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
file(GLOB_RECURSE CORE_SOURCES "*.cpp")
2+
file(GLOB RULE_PREPROCESSING_SOURCES "rule_preprocessing/*.cpp")
23
list(REMOVE_ITEM CORE_SOURCES
34
"${CMAKE_CURRENT_SOURCE_DIR}/cpp2rust.cpp"
4-
"${CMAKE_CURRENT_SOURCE_DIR}/cpp_rule_preprocessor.cpp"
5+
${RULE_PREPROCESSING_SOURCES}
56
)
67

78
add_library(cpp2rust_core STATIC ${CORE_SOURCES})
@@ -17,6 +18,8 @@ target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_C_COMPILER=\"${CMAKE_C_
1718
target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_CXX_COMPILER=\"${CMAKE_CXX_COMPILER}\"")
1819
target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_RESOURCE_DIR=\"${LLVM_LIBRARY_DIR}/clang/${LLVM_VERSION_MAJOR}\"")
1920
target_compile_definitions(cpp2rust_core PUBLIC "-DCOMPAT_INCLUDE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/compat\"")
21+
target_compile_definitions(cpp2rust_core PUBLIC "-DRULES_SRC_DIR=\"${PROJECT_SOURCE_DIR}/rules\"")
22+
target_compile_definitions(cpp2rust_core PUBLIC "-DRULES_INDEX_DIR=\"${CMAKE_BINARY_DIR}/rules_index\"")
2023
target_compile_definitions(cpp2rust_core PUBLIC "-DRUST_STABLE_VERSION=\"${RUST_STABLE_VERSION}\"")
2124

2225
if (APPLE)
@@ -34,5 +37,10 @@ target_link_libraries(cpp2rust_core PUBLIC ${llvm_libs})
3437
add_clang_executable(cpp2rust PARTIAL_SOURCES_INTENDED cpp2rust.cpp)
3538
target_link_libraries(cpp2rust PRIVATE cpp2rust_core)
3639

37-
add_clang_executable(cpp-rule-preprocessor PARTIAL_SOURCES_INTENDED cpp_rule_preprocessor.cpp)
40+
add_clang_executable(cpp-rule-preprocessor PARTIAL_SOURCES_INTENDED
41+
rule_preprocessing/cpp_rule_preprocessor.cpp)
3842
target_link_libraries(cpp-rule-preprocessor PRIVATE cpp2rust_core)
43+
44+
add_clang_executable(cpp-rule-indexer PARTIAL_SOURCES_INTENDED
45+
rule_preprocessing/cpp_rule_indexer.cpp)
46+
target_link_libraries(cpp-rule-indexer PRIVATE cpp2rust_core)

‎cpp2rust/ast_consumer.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33

44
#include "ast_consumer.h"
55

6+
#include <llvm/Support/ErrorHandling.h>
7+
68
#include "converter/converter.h"
79

810
namespace cpp2rust {
911
void ASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
12+
if (CI_.getDiagnostics().hasErrorOccurred()) {
13+
llvm::report_fatal_error(
14+
"the translation unit and its loaded rules do not compile");
15+
}
1016
auto converter = CreateConverter(rs_code_, ctx, model_, rules_dir_);
1117
converter->SetSema(CI_.getSema());
1218
if (first_) {

‎cpp2rust/converter/converter.cpp‎

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,49 +1030,7 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {
10301030
}
10311031

10321032
void Converter::DefineImplicitMembers(clang::CXXRecordDecl *decl) {
1033-
clang::Scope tu_scope(nullptr, clang::Scope::DeclScope,
1034-
sema_->getDiagnostics());
1035-
tu_scope.setEntity(ctx_.getTranslationUnitDecl());
1036-
auto *saved_tu_scope = std::exchange(sema_->TUScope, &tu_scope);
1037-
sema_->ForceDeclarationOfImplicitMembers(decl);
1038-
for (auto ctor : decl->ctors()) {
1039-
if (ctor->isCopyConstructor() && ctor->isImplicit() &&
1040-
!ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted()) {
1041-
sema_->DefineImplicitCopyConstructor(decl->getLocation(), ctor);
1042-
}
1043-
if (ctor->isMoveConstructor() && !ctor->isUserProvided() &&
1044-
!ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted() &&
1045-
!HasDefaultedCopyConstructor(decl)) {
1046-
sema_->DefineImplicitMoveConstructor(decl->getLocation(), ctor);
1047-
}
1048-
}
1049-
for (auto *method : decl->methods()) {
1050-
if (method->isMoveAssignmentOperator() && !method->isUserProvided() &&
1051-
!method->doesThisDeclarationHaveABody() && !method->isDeleted() &&
1052-
!HasDefaultedCopyAssignment(decl)) {
1053-
sema_->DefineImplicitMoveAssignment(decl->getLocation(), method);
1054-
}
1055-
}
1056-
auto define_defaulted_comparison = [&](clang::FunctionDecl *fn) {
1057-
if (!fn || !IsComparisonOperator(fn) || !fn->isDefaulted() ||
1058-
fn->doesThisDeclarationHaveABody()) {
1059-
return;
1060-
}
1061-
#if CLANG_VERSION_MAJOR >= 24
1062-
auto kind = fn->getDefaultedComparisonKind();
1063-
#else
1064-
auto kind = sema_->getDefaultedComparisonKind(fn);
1065-
#endif
1066-
sema_->DefineDefaultedComparison(decl->getLocation(), fn, kind);
1067-
};
1068-
for (auto *method : decl->methods()) {
1069-
define_defaulted_comparison(method);
1070-
}
1071-
for (auto *friend_decl : decl->friends()) {
1072-
define_defaulted_comparison(clang::dyn_cast_or_null<clang::FunctionDecl>(
1073-
friend_decl->getFriendDecl()));
1074-
}
1075-
sema_->TUScope = saved_tu_scope;
1033+
cpp2rust::DefineImplicitMembers(*sema_, decl);
10761034
}
10771035

10781036
bool Converter::VisitCXXMethodDecl(clang::CXXMethodDecl *decl) {

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "converter/converter_lib.h"
55

6+
#include <clang/AST/DeclFriend.h>
67
#include <clang/AST/DeclTemplate.h>
78
#include <clang/AST/ExprCXX.h>
89
#include <clang/AST/ExprConcepts.h>
@@ -1460,6 +1461,52 @@ bool IsComparisonOperator(const clang::FunctionDecl *fn) {
14601461
}
14611462
}
14621463

1464+
void DefineImplicitMembers(clang::Sema &sema, clang::CXXRecordDecl *decl) {
1465+
clang::Scope tu_scope(nullptr, clang::Scope::DeclScope,
1466+
sema.getDiagnostics());
1467+
tu_scope.setEntity(sema.Context.getTranslationUnitDecl());
1468+
auto *saved_tu_scope = std::exchange(sema.TUScope, &tu_scope);
1469+
sema.ForceDeclarationOfImplicitMembers(decl);
1470+
for (auto ctor : decl->ctors()) {
1471+
if (ctor->isCopyConstructor() && ctor->isImplicit() &&
1472+
!ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted()) {
1473+
sema.DefineImplicitCopyConstructor(decl->getLocation(), ctor);
1474+
}
1475+
if (ctor->isMoveConstructor() && !ctor->isUserProvided() &&
1476+
!ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted() &&
1477+
!HasDefaultedCopyConstructor(decl)) {
1478+
sema.DefineImplicitMoveConstructor(decl->getLocation(), ctor);
1479+
}
1480+
}
1481+
for (auto *method : decl->methods()) {
1482+
if (method->isMoveAssignmentOperator() && !method->isUserProvided() &&
1483+
!method->doesThisDeclarationHaveABody() && !method->isDeleted() &&
1484+
!HasDefaultedCopyAssignment(decl)) {
1485+
sema.DefineImplicitMoveAssignment(decl->getLocation(), method);
1486+
}
1487+
}
1488+
auto define_defaulted_comparison = [&](clang::FunctionDecl *fn) {
1489+
if (!fn || !IsComparisonOperator(fn) || !fn->isDefaulted() ||
1490+
fn->doesThisDeclarationHaveABody()) {
1491+
return;
1492+
}
1493+
#if CLANG_VERSION_MAJOR >= 24
1494+
auto kind = fn->getDefaultedComparisonKind();
1495+
#else
1496+
auto kind = sema.getDefaultedComparisonKind(fn);
1497+
#endif
1498+
sema.DefineDefaultedComparison(decl->getLocation(), fn, kind);
1499+
};
1500+
for (auto *method : decl->methods()) {
1501+
define_defaulted_comparison(method);
1502+
}
1503+
for (auto *friend_decl : decl->friends()) {
1504+
define_defaulted_comparison(clang::dyn_cast_or_null<clang::FunctionDecl>(
1505+
friend_decl->getFriendDecl()));
1506+
}
1507+
sema.TUScope = saved_tu_scope;
1508+
}
1509+
14631510
bool IsEmittableMethod(clang::CXXMethodDecl *method) {
14641511
if (method->isDeleted()) {
14651512
return false;

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ bool IsConvertibleCXXRecordDecl(const clang::CXXRecordDecl *decl);
133133
bool IsConvertibleCXXMethodDecl(const clang::CXXMethodDecl *decl);
134134

135135
bool IsComparisonOperator(const clang::FunctionDecl *fn);
136+
137+
void DefineImplicitMembers(clang::Sema &sema, clang::CXXRecordDecl *decl);
138+
136139
bool IsEmittableMethod(clang::CXXMethodDecl *method);
137140

138141
bool IsMethodOnPtr(const clang::CXXMethodDecl *method);

‎cpp2rust/converter/printer.cpp‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,20 @@ std::string ToString(clang::ASTContext &ctx, clang::QualType qual_type,
134134
}
135135
if (const auto *typedef_type = t->getAs<clang::TypedefType>()) {
136136
if (t.getCanonicalType()->isBuiltinType()) {
137-
return typedef_type->getDecl()->getNameAsString();
137+
auto decl = typedef_type->getDecl();
138+
if (auto record =
139+
clang::dyn_cast<clang::RecordDecl>(decl->getDeclContext());
140+
record && !record->isDependentContext()) {
141+
return ToString(ctx, ctx.getCanonicalTagType(record)) +
142+
"::" + decl->getNameAsString();
143+
}
144+
if (decl->getDeclContext()->getRedeclContext()->isNamespace()) {
145+
std::string name;
146+
llvm::raw_string_ostream os(name);
147+
decl->printQualifiedName(os, getPrintPolicy(ctx));
148+
return name;
149+
}
150+
return decl->getNameAsString();
138151
}
139152
} else if (const auto *predef = t->getAs<clang::PredefinedSugarType>()) {
140153
return predef->getIdentifier()->getName().str();

0 commit comments

Comments
 (0)