From c371a7ef1c2fd3e382751d3ee7ef6836b9e029e4 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 14 Oct 2026 10:08:56 +0100 Subject: [PATCH] Work --- CMakeLists.txt | 47 +- cpp2rust/CMakeLists.txt | 5 +- cpp2rust/ast_consumer.cpp | 2 + cpp2rust/converter/converter.cpp | 54 +- cpp2rust/converter/converter.h | 8 +- cpp2rust/converter/converter_lib.cpp | 18 +- cpp2rust/converter/converter_lib.h | 5 +- cpp2rust/converter/factory.cpp | 2 - cpp2rust/converter/mapper.cpp | 32 +- .../converter/models/converter_refcount.cpp | 18 +- .../converter/models/converter_refcount.h | 2 + cpp2rust/converter/rules/decls.cpp | 94 ++ cpp2rust/converter/rules/decls.h | 47 + cpp2rust/converter/rules/expr_matcher.cpp | 378 ++++++ cpp2rust/converter/rules/expr_matcher.h | 23 + cpp2rust/converter/rules/matcher.cpp | 22 +- cpp2rust/converter/rules/matcher.h | 23 +- cpp2rust/converter/rules/registry.cpp | 446 +++++-- cpp2rust/converter/rules/registry.h | 29 +- cpp2rust/converter/rules/rule_keys.cpp | 288 +++++ cpp2rust/converter/rules/rule_keys.h | 73 ++ cpp2rust/converter/rules/rules_loader.cpp | 72 +- cpp2rust/converter/rules/rules_loader.h | 4 + cpp2rust/converter/rules/string_matcher.cpp | 405 ------ cpp2rust/converter/rules/type_matcher.cpp | 171 +++ cpp2rust/converter/rules/type_matcher.h | 28 + cpp2rust/converter/translation_rule.cpp | 252 +--- cpp2rust/converter/translation_rule.h | 29 +- cpp2rust/cpp2rust.cpp | 10 +- .../rule_preprocessing/cpp_rule_indexer.cpp | 163 ++- .../cpp_rule_preprocessor.cpp | 1151 ----------------- docs/src/rules/format.md | 17 + rules/algorithm/src.cpp | 18 +- rules/array/src.cpp | 4 +- rules/brotli/src.c | 78 +- rules/builtin/src.c | 4 +- rules/builtin_types/cxxflags | 1 - rules/builtin_types/src.c | 50 +- rules/builtin_types/src.cpp | 20 +- .../src_cxx20.cpp} | 5 +- rules/builtin_types/tgt_refcount.rs | 336 ----- rules/builtin_types/tgt_unsafe.rs | 336 ----- rules/common/traits.h | 58 + rules/compare/cxxflags | 1 - rules/compare/src.cpp | 28 - rules/compare/src_cxx20.cpp | 36 + rules/cstddef/src.c | 4 - rules/cstddef/src.cpp | 13 - rules/cstddef/src_cxx17.cpp | 14 + rules/cstddef/tgt_refcount.rs | 28 - rules/cstddef/tgt_unsafe.rs | 24 - rules/cstdlib/src.c | 2 +- rules/deque/src.cpp | 7 +- rules/deque/tgt_refcount.rs | 2 + rules/deque_cxx11/cxxflags | 1 - rules/deque_cxx11/src.cpp | 12 - rules/eventfd/src.c | 6 +- rules/initializer_list/src.cpp | 3 +- rules/iostream/src.cpp | 4 - rules/iostream/tgt_refcount.rs | 13 +- rules/iostream/tgt_unsafe.rs | 25 +- rules/ip/src.c | 12 +- rules/limits/src.cpp | 4 +- rules/map/src.cpp | 91 +- rules/new/src.cpp | 4 - rules/new/src_cxx17.cpp | 6 + rules/pair/src.cpp | 42 +- rules/pair/tgt_refcount.rs | 42 +- rules/pair/tgt_unsafe.rs | 26 +- rules/rustls/src.c | 53 +- rules/rustls/tgt_unsafe.rs | 81 +- rules/socket/src.c | 20 +- rules/stdio/src.c | 6 +- rules/string/src.cpp | 12 +- rules/string/src_cxx17.cpp | 6 + rules/time/src.c | 6 +- rules/unique_ptr/src.cpp | 23 +- rules/unique_ptr/src_cxx14.cpp | 17 + rules/unique_ptr/tgt_refcount.rs | 4 + rules/unique_ptr/tgt_unsafe.rs | 4 + rules/vector/src.cpp | 344 +---- rules/vector/tgt_refcount.rs | 174 +-- rules/vector/tgt_unsafe.rs | 223 +--- rules/vector_cxx11/cxxflags | 1 - rules/vector_cxx11/src.cpp | 16 - rules/vector_cxx11/tgt_unsafe.rs | 12 - tests/unit/out/refcount/array_reference.rs | 8 +- tests/unit/out/refcount/cout_alias.rs | 8 +- tests/unit/out/refcount/cstring.rs | 39 +- tests/unit/out/refcount/default_in_statics.rs | 2 +- tests/unit/out/refcount/ifaddrs.rs | 2 +- tests/unit/out/refcount/netdb.rs | 21 +- tests/unit/out/refcount/overload_mangling.rs | 7 +- tests/unit/out/refcount/pointer_array.rs | 2 +- tests/unit/out/refcount/random.rs | 49 +- tests/unit/out/refcount/stdio_nofd.rs | 12 +- tests/unit/out/refcount/string2.rs | 10 + tests/unit/out/refcount/string_h.rs | 42 +- tests/unit/out/refcount/vector.rs | 26 +- .../out/refcount/vector_with_allocator.rs | 70 +- tests/unit/out/refcount/z_bit_cast.rs | 2 +- tests/unit/out/unsafe/bool_printing.rs | 64 +- tests/unit/out/unsafe/char_printing.rs | 74 +- tests/unit/out/unsafe/char_printing_cerr.rs | 74 +- tests/unit/out/unsafe/cout_alias.rs | 8 +- tests/unit/out/unsafe/reference_wrapper.rs | 46 +- tests/unit/out/unsafe/string2.rs | 12 + tests/unit/out/unsafe/swap_extended.rs | 80 +- tests/unit/out/unsafe/vector.rs | 21 + .../unit/out/unsafe/vector_with_allocator.rs | 70 +- tests/unit/string2.cpp | 3 + tests/unit/vector.cpp | 1 - 112 files changed, 2539 insertions(+), 4494 deletions(-) create mode 100644 cpp2rust/converter/rules/decls.cpp create mode 100644 cpp2rust/converter/rules/decls.h create mode 100644 cpp2rust/converter/rules/expr_matcher.cpp create mode 100644 cpp2rust/converter/rules/expr_matcher.h create mode 100644 cpp2rust/converter/rules/rule_keys.cpp create mode 100644 cpp2rust/converter/rules/rule_keys.h delete mode 100644 cpp2rust/converter/rules/string_matcher.cpp create mode 100644 cpp2rust/converter/rules/type_matcher.cpp create mode 100644 cpp2rust/converter/rules/type_matcher.h delete mode 100644 cpp2rust/rule_preprocessing/cpp_rule_preprocessor.cpp delete mode 100644 rules/builtin_types/cxxflags rename rules/{deque_cxx11/tgt_unsafe.rs => builtin_types/src_cxx20.cpp} (55%) create mode 100644 rules/common/traits.h delete mode 100644 rules/compare/cxxflags delete mode 100644 rules/compare/src.cpp create mode 100644 rules/compare/src_cxx20.cpp create mode 100644 rules/cstddef/src_cxx17.cpp delete mode 100644 rules/cstddef/tgt_refcount.rs delete mode 100644 rules/deque_cxx11/cxxflags delete mode 100644 rules/deque_cxx11/src.cpp create mode 100644 rules/new/src_cxx17.cpp create mode 100644 rules/string/src_cxx17.cpp create mode 100644 rules/unique_ptr/src_cxx14.cpp delete mode 100644 rules/vector_cxx11/cxxflags delete mode 100644 rules/vector_cxx11/src.cpp delete mode 100644 rules/vector_cxx11/tgt_unsafe.rs diff --git a/CMakeLists.txt b/CMakeLists.txt index 640dd484b..933b9a235 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -162,8 +162,6 @@ add_custom_target("check" DEPENDS check-libcc2rs check-libcc2rs-macros check-unit ) -set(RULES_IR_DIR "${CMAKE_BINARY_DIR}/rules") - set(RUSTLS_HEADER "${PROJECT_SOURCE_DIR}/rules/rustls/rustls.h") set(BROTLI_HEADERS "${PROJECT_SOURCE_DIR}/rules/brotli/brotli/decode.h" @@ -222,45 +220,33 @@ file(GLOB rule_subdirs CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/rules/*) file(GLOB rule_common_headers CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/rules/common/*.h) file(GLOB rule_indexer_sources CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/cpp2rust/rule_preprocessing/* - ${PROJECT_SOURCE_DIR}/cpp2rust/converter/rules/rules_loader.*) + ${PROJECT_SOURCE_DIR}/cpp2rust/converter/rules/rules_loader.* + ${PROJECT_SOURCE_DIR}/cpp2rust/converter/rules/decls.*) set(_common_headers) foreach(_header IN LISTS rule_common_headers) list(APPEND _common_headers --common-header=${_header}) endforeach() -set(cpp_rules_ir_outputs) -set(cpp_rules_index_inputs) -set(cpp_rules_index_outputs ${CMAKE_BINARY_DIR}/rules_index/c.json ${CMAKE_BINARY_DIR}/rules_index/cpp.json) +set(rules_index_parts) +set(rules_index_parts_dir ${CMAKE_BINARY_DIR}/rules_index/parts) +set(RULES_IR_DIR "${CMAKE_BINARY_DIR}/rules") set(rust_rules_ir_outputs) foreach(_rule_dir IN LISTS rule_subdirs) - file(GLOB _cpp_srcs ${_rule_dir}/src.c ${_rule_dir}/src.cpp) + file(GLOB _cpp_srcs ${_rule_dir}/src.c ${_rule_dir}/src.cpp ${_rule_dir}/src_cxx*.cpp) if(NOT _cpp_srcs) continue() endif() get_filename_component(_rule_name ${_rule_dir} NAME) - set(_out_dir ${RULES_IR_DIR}/${_rule_name}) - set(_cpp_out ${_out_dir}/ir_src.json) - set(_cxxflags) - set(_cxxflags_file) - if(EXISTS ${_rule_dir}/cxxflags) - set(_cxxflags_file ${_rule_dir}/cxxflags) - file(STRINGS ${_cxxflags_file} _flags) - foreach(_flag IN LISTS _flags) - list(APPEND _cxxflags --cxxflags=${_flag}) - endforeach() - endif() + set(_part ${rules_index_parts_dir}/${_rule_name}.json) add_custom_command( - OUTPUT ${_cpp_out} - COMMAND ${CMAKE_COMMAND} -E make_directory ${_out_dir} - COMMAND $ --dir ${_rule_dir} --out ${_cpp_out} ${_cxxflags} - DEPENDS ${_cpp_srcs} ${_cxxflags_file} ${rule_common_headers} ${PROJECT_SOURCE_DIR}/cpp2rust/rule_preprocessing/cpp_rule_preprocessor.cpp ${RUSTLS_HEADER} ${BROTLI_HEADERS} + OUTPUT ${_part} + COMMAND ${CMAKE_COMMAND} -E make_directory ${rules_index_parts_dir} + COMMAND $ --dir ${_rule_dir} -o ${_part} + DEPENDS ${_cpp_srcs} ${rule_common_headers} ${rule_indexer_sources} ${RUSTLS_HEADER} ${BROTLI_HEADERS} VERBATIM ) - list(APPEND cpp_rules_ir_outputs ${_cpp_out}) - - if(NOT _rule_name STREQUAL "builtin_types") - list(APPEND cpp_rules_index_inputs ${_cpp_srcs} ${_cxxflags_file}) - endif() + list(APPEND rules_index_parts ${_part}) + set(_out_dir ${RULES_IR_DIR}/${_rule_name}) set(_rust_srcs) set(_rust_out) if(EXISTS ${_rule_dir}/tgt_unsafe.rs) @@ -289,12 +275,11 @@ foreach(_rule_dir IN LISTS rule_subdirs) list(APPEND rust_rules_ir_outputs ${_rust_out}) endforeach() -add_custom_target("preprocess-cpp-rules" ALL DEPENDS ${cpp_rules_ir_outputs}) - +set(cpp_rules_index_outputs ${CMAKE_BINARY_DIR}/rules_index/c.json ${CMAKE_BINARY_DIR}/rules_index/cpp.json) add_custom_command( OUTPUT ${cpp_rules_index_outputs} - COMMAND $ --exclude builtin_types ${_common_headers} - DEPENDS ${cpp_rules_index_inputs} ${rule_common_headers} ${rule_indexer_sources} ${RUSTLS_HEADER} ${BROTLI_HEADERS} + COMMAND $ ${_common_headers} -o ${CMAKE_BINARY_DIR}/rules_index ${rules_index_parts} + DEPENDS ${rules_index_parts} ${rule_common_headers} ${rule_indexer_sources} VERBATIM ) diff --git a/cpp2rust/CMakeLists.txt b/cpp2rust/CMakeLists.txt index 3b7760c25..bdeb41c6d 100644 --- a/cpp2rust/CMakeLists.txt +++ b/cpp2rust/CMakeLists.txt @@ -20,6 +20,7 @@ target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_RESOURCE_DIR=\"${LLVM_L target_compile_definitions(cpp2rust_core PUBLIC "-DCOMPAT_INCLUDE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/compat\"") target_compile_definitions(cpp2rust_core PUBLIC "-DRULES_SRC_DIR=\"${PROJECT_SOURCE_DIR}/rules\"") target_compile_definitions(cpp2rust_core PUBLIC "-DRULES_INDEX_DIR=\"${CMAKE_BINARY_DIR}/rules_index\"") +target_compile_definitions(cpp2rust_core PUBLIC "-DRULES_IR_DIR=\"${CMAKE_BINARY_DIR}/rules\"") target_compile_definitions(cpp2rust_core PUBLIC "-DRUST_STABLE_VERSION=\"${RUST_STABLE_VERSION}\"") if (APPLE) @@ -37,10 +38,6 @@ target_link_libraries(cpp2rust_core PUBLIC ${llvm_libs}) add_clang_executable(cpp2rust PARTIAL_SOURCES_INTENDED cpp2rust.cpp) target_link_libraries(cpp2rust PRIVATE cpp2rust_core) -add_clang_executable(cpp-rule-preprocessor PARTIAL_SOURCES_INTENDED - rule_preprocessing/cpp_rule_preprocessor.cpp) -target_link_libraries(cpp-rule-preprocessor PRIVATE cpp2rust_core) - add_clang_executable(cpp-rule-indexer PARTIAL_SOURCES_INTENDED rule_preprocessing/cpp_rule_indexer.cpp) target_link_libraries(cpp-rule-indexer PRIVATE cpp2rust_core) diff --git a/cpp2rust/ast_consumer.cpp b/cpp2rust/ast_consumer.cpp index 50624db78..00a476097 100644 --- a/cpp2rust/ast_consumer.cpp +++ b/cpp2rust/ast_consumer.cpp @@ -6,6 +6,7 @@ #include #include "converter/converter.h" +#include "converter/rules/registry.h" namespace cpp2rust { void ASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { @@ -13,6 +14,7 @@ void ASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { llvm::report_fatal_error( "the translation unit and its loaded rules do not compile"); } + RuleRegistry::Load(CI_.getSema(), model_); auto converter = CreateConverter(rs_code_, ctx, model_, rules_dir_); converter->SetSema(CI_.getSema()); if (first_) { diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index aa13f6639..c3c60c86a 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -134,6 +134,10 @@ bool Converter::Convert(clang::QualType qual_type) { return TraverseType(qual_type); } +std::string Converter::GetUnboxedTypeAsString(clang::QualType qual_type) { + return ToString(qual_type); +} + bool Converter::ConvertMappedType(clang::QualType qual_type) { std::string type_as_string = Mapper::Map(*this, qual_type); if (type_as_string == token::kIgnoreRule) { @@ -181,7 +185,11 @@ bool Converter::VisitRecordType(clang::RecordType *type) { } StrCat(GetRecordName(decl)); - RuleRegistry::AddRuleForUserDefinedType(ctx_, decl); + return false; +} + +bool Converter::VisitEnumType(clang::EnumType *type) { + StrCat(GetRecordName(type->getDecl())); return false; } @@ -731,11 +739,19 @@ bool Converter::IsPassThroughRule(clang::Expr *expr) const { rule->body[0]); } -bool Converter::RecordDerivesCopy(const clang::RecordDecl *decl) const { - auto *derives = - RuleRegistry::MappedDerives(ctx_, ctx_.getCanonicalTagType(decl)); - return derives && - std::find(derives->begin(), derives->end(), "Copy") != derives->end(); +bool Converter::RecordDerivesCopy(const clang::RecordDecl *decl) { + if (auto rule = + RuleRegistry::Search(ctx_, ctx_.getCanonicalTagType(decl)).first) { + auto &derives = rule->type_info.derives; + return std::find(derives.begin(), derives.end(), "Copy") != derives.end(); + } + decl = decl->getDefinition(); + if (!decl) { + return false; + } + auto attrs = GetStructAttributes(decl); + return std::find(attrs.begin(), attrs.end(), std::string_view("Copy")) != + attrs.end(); } bool Converter::RecordHasCopyableFields(const clang::RecordDecl *decl) { @@ -794,7 +810,6 @@ bool Converter::VisitRecordDecl(clang::RecordDecl *decl) { return false; } - RuleRegistry::AddRuleForUserDefinedType(ctx_, decl); EmitRustStructOrUnion(decl); return false; @@ -861,9 +876,6 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) { EmitReprC(decl); } auto attrs = GetStructAttributes(decl); - RuleRegistry::SetDerives( - ctx_, ctx_.getCanonicalTagType(decl), - std::vector(attrs.begin(), attrs.end())); StrCat("#[derive("); for (auto *attr : attrs) { StrCat(attr, ','); @@ -961,9 +973,6 @@ void Converter::EmitReprC(clang::RecordDecl *decl) { void Converter::EmitRustUnion(clang::RecordDecl *decl) { EmitReprC(decl); auto attrs = GetStructAttributes(decl); - RuleRegistry::SetDerives( - ctx_, ctx_.getCanonicalTagType(decl), - std::vector(attrs.begin(), attrs.end())); StrCat("#[derive("); for (auto *attr : attrs) { StrCat(attr, ','); @@ -987,7 +996,6 @@ void Converter::EmitRustUnion(clang::RecordDecl *decl) { bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) { decl->dump(log()); - RuleRegistry::AddRuleForUserDefinedType(ctx_, decl); if (!IsConvertibleCXXRecordDecl(decl)) { return false; } @@ -1943,7 +1951,7 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) { } auto ty = GetReturnTypeOfFunction(expr); - auto ref = clang::dyn_cast(ty); + auto ref = ty->getAs(); if (ref && !isAddrOf() && !isVoid()) { { @@ -3198,6 +3206,10 @@ std::string Converter::ConvertDeclRef(clang::Expr *expr, if (ShouldReplaceWithMappedBody(decl)) { if (auto str = GetMappedAsString(expr); !str.empty()) { + if (RuleRegistry::ReturnsReference(expr) && !isAddrOf()) { + return std::format("({}{})", GetPointerDerefPrefix(expr->getType()), + str); + } return str; } } @@ -3950,7 +3962,6 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) { if (Mapper::Contains(ctx_, ctx_.getCanonicalTagType(decl))) { return false; } - RuleRegistry::AddRuleForUserDefinedType(ctx_, decl); auto name = GetRecordName(decl); StrCat(std::format("pub type {} = {};", name, GetUnsafeTypeAsString(decl->getIntegerType()))); @@ -5316,13 +5327,10 @@ std::string Converter::ConvertInitFragment(clang::Expr *expr, const std::vector &all_args) { const auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, expr); - assert(tgt_ir && tgt_ir->init_type.valid()); - auto *callee = clang::cast(expr)->getDirectCallee(); - assert(callee); - auto type = GetSema() - .getTemplateInstantiationArgs(callee)(tgt_ir->init_type.depth, - tgt_ir->init_type.index) - .getAsType(); + if (!tgt_ir || !tgt_ir->usesInit()) { + llvm::report_fatal_error("expression rule does not use Init"); + } + auto type = RuleRegistry::GetInitType(expr); Buffer buf(*this); ConvertConstructFromArgs( diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index bd3773230..a1aae4935 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -50,6 +50,8 @@ class Converter : public clang::RecursiveASTVisitor { clang::ASTContext &GetASTContext() { return ctx_; } + virtual std::string GetUnboxedTypeAsString(clang::QualType qual_type); + bool VisitRecoveryExpr(clang::RecoveryExpr *expr); virtual void EmitFilePreamble(); @@ -63,6 +65,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitRecordType(clang::RecordType *type); + bool VisitEnumType(clang::EnumType *type); + virtual bool VisitConstantArrayType(clang::ConstantArrayType *type); virtual bool VisitIncompleteArrayType(clang::IncompleteArrayType *type); @@ -352,7 +356,7 @@ class Converter : public clang::RecursiveASTVisitor { // Option implements Copy virtual bool FunctionPointerImplementsCopy() const { return true; } - bool TypeIsCopyable(clang::QualType ty) const { + bool TypeIsCopyable(clang::QualType ty) { if (ty->isFunctionPointerType() || ty->isFunctionType()) { return FunctionPointerImplementsCopy(); } @@ -774,7 +778,7 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool TypeDerivesDefault(clang::QualType qual_type); - bool RecordDerivesCopy(const clang::RecordDecl *decl) const; + bool RecordDerivesCopy(const clang::RecordDecl *decl); bool IsPassThroughRule(clang::Expr *expr) const; diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index c11ea4977..d7553038f 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -317,7 +317,7 @@ bool IsAbstractUserClass(const clang::RecordDecl *decl) { definition->isAbstract(); } -bool RefersToUserDefinedDecl(const clang::Expr *expr) { +bool ExprHasUserDefinedDecl(const clang::Expr *expr) { expr = expr->IgnoreParenImpCasts(); const clang::Decl *decl = nullptr; if (const auto *call = llvm::dyn_cast(expr)) { @@ -1070,7 +1070,7 @@ const char *AccessSpecifierAsString(clang::AccessSpecifier spec) { clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr) { if (auto *decl = expr->getCalleeDecl()) { if (auto *fn = decl->getAsFunction()) { - return fn->getReturnType().getCanonicalType(); + return fn->getReturnType(); } } @@ -1078,7 +1078,7 @@ clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr) { if (auto *ptr_ty = callee_ty->getAs()) { if (auto *fn_ty = ptr_ty->getPointeeType()->getAs()) { - return fn_ty->getReturnType().getCanonicalType(); + return fn_ty->getReturnType(); } } @@ -1624,18 +1624,6 @@ clang::CXXConstructExpr *MakeConstructExpr(clang::ASTContext &ctx, clang::SourceRange()); } -std::vector -GetNestedStructs(const clang::CXXRecordDecl *decl) { - std::vector nested_record_decls; - for (auto *d : decl->decls()) { - if (auto *rec = clang::dyn_cast(d); - rec && !rec->isImplicit()) { - nested_record_decls.push_back(rec); - } - } - return nested_record_decls; -} - std::optional> GetTemplateArgs(clang::QualType qual_type, clang::Expr *expr) { if (auto ty = clang::dyn_cast(qual_type)) { diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 384455d50..b118a1807 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -70,7 +70,7 @@ bool IsUserDefinedDecl(const clang::Decl *decl); bool IsAbstractUserClass(const clang::RecordDecl *decl); -bool RefersToUserDefinedDecl(const clang::Expr *expr); +bool ExprHasUserDefinedDecl(const clang::Expr *expr); bool IsUnsignedArithOp(const clang::BinaryOperator *expr); @@ -271,9 +271,6 @@ clang::CXXConstructExpr *MakeConstructExpr(clang::ASTContext &ctx, clang::CXXConstructorDecl *ctor, llvm::ArrayRef args); -std::vector -GetNestedStructs(const clang::CXXRecordDecl *decl); - std::optional> GetTemplateArgs(clang::QualType qual_type, clang::Expr *expr = nullptr); diff --git a/cpp2rust/converter/factory.cpp b/cpp2rust/converter/factory.cpp index 4a34e2c9b..7c8380fe3 100644 --- a/cpp2rust/converter/factory.cpp +++ b/cpp2rust/converter/factory.cpp @@ -4,14 +4,12 @@ #include "converter/factory.h" #include "converter/models/converter_refcount.h" -#include "converter/rules/registry.h" namespace cpp2rust { std::unique_ptr CreateConverter(std::string &rs_code, clang::ASTContext &ctx, Model model, const std::string &rules_dir) { - RuleRegistry::Load(model, rules_dir); switch (model) { case Model::kUnsafe: return std::make_unique(rs_code, ctx); diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index f65fef5b7..963ef8298 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -30,7 +30,7 @@ bool Contains(clang::ASTContext &ctx, const clang::Expr *expr) { std::string MapFunctionName(clang::ASTContext &ctx, const clang::FunctionDecl *decl) { assert(decl); - if (!IsUserDefinedDecl(decl) && Matcher::HasRuleNamed(ctx, decl)) { + if (!IsUserDefinedDecl(decl) && RuleRegistry::HasExprRule(decl)) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), RuleRegistry::CurrentModel() == Model::kRefCount ? "refcount" @@ -42,36 +42,31 @@ std::string MapFunctionName(clang::ASTContext &ctx, std::string InstantiateTemplate(Converter &converter, const clang::Expr *expr, unsigned n) { auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), expr); - auto text = std::format("T{}", n); if (!rule) { - return text; + return std::format("T{}", n); } - auto &ty = subs.at(n - 1); - if (ty) { - ty = Matcher::MapBinding(subs, n - 1); - } - return Matcher::InstantiateTgt(subs, text); + return Matcher::MapBinding(converter, subs, n - 1); } std::string Map(clang::ASTContext &ctx, clang::QualType qual_type) { - auto [rule, subs] = RuleRegistry::Search(ctx, qual_type); - if (rule) { - return Matcher::InstantiateTgt(Matcher::MapBindings(subs), - rule->type_info.type); - } - return {}; + auto rule = RuleRegistry::Search(ctx, qual_type).first; + return rule ? rule->type_info.type : std::string(); } std::string Map(Converter &converter, clang::QualType qual_type) { - return Map(converter.GetASTContext(), qual_type); + auto [rule, subs] = + RuleRegistry::Search(converter.GetASTContext(), qual_type); + if (rule) { + return Matcher::InstantiateTgt(converter, subs, rule->type_info.type); + } + return {}; } std::string MapInitializer(Converter &converter, clang::QualType qual_type) { auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), qual_type); if (rule && !rule->initializer.empty()) { - return Matcher::InstantiateTgt(Matcher::MapBindings(subs), - rule->initializer); + return Matcher::InstantiateTgt(converter, subs, rule->initializer); } return {}; } @@ -79,8 +74,7 @@ std::string MapInitializer(Converter &converter, clang::QualType qual_type) { std::string GetParamType(Converter &converter, const clang::Expr *expr, unsigned index) { auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), expr); - return Matcher::InstantiateTgt(Matcher::MapBindings(subs), - rule->params.at(index).type); + return Matcher::InstantiateTgt(converter, subs, rule->params.at(index).type); } } // namespace cpp2rust::Mapper diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 5edffdeb3..76dd4c432 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -218,6 +218,12 @@ std::string ConverterRefCount::BoxValue(std::string &&str) const { std::unreachable(); } +std::string +ConverterRefCount::GetUnboxedTypeAsString(clang::QualType qual_type) { + PushConversionKind push(*this, ConversionKind::Unboxed); + return ToString(qual_type); +} + bool ConverterRefCount::Convert(clang::QualType qual_type) { // Catch va_list before desugaring if (IsVaListType(qual_type)) { @@ -687,9 +693,6 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) { auto name = GetRecordName(decl); auto attrs = GetStructAttributes(decl); - RuleRegistry::SetDerives( - ctx_, ctx_.getCanonicalTagType(decl), - std::vector(attrs.begin(), attrs.end())); StrCat("#[derive("); for (auto *attr : attrs) { StrCat(attr, ','); @@ -1315,7 +1318,7 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) { } auto ty = GetReturnTypeOfFunction(expr); - auto ref = clang::dyn_cast(ty); + auto ref = ty->getAs(); if (ref && !isAddrOf() && !isVoid()) { if (isLValue()) { @@ -1505,7 +1508,12 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { PushParen paren(*this); StrCat(ConvertPointer(sub_expr)); if (!IsReferenceType(sub_expr)) { - StrCat(keyword::kAs, ToString(expr->getType())); + StrCat(keyword::kAs); + if (expr->getType()->getPointeeType()->isBuiltinType()) { + StrCat(std::format("Ptr::<{}>", ConvertPointeeType(expr->getType()))); + } else { + StrCat(ToString(expr->getType())); + } } return false; } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index b2e54bd63..9f8880df3 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -17,6 +17,8 @@ class ConverterRefCount final : public Converter { void EmitFilePreamble() override; + std::string GetUnboxedTypeAsString(clang::QualType qual_type) override; + static void EmitMethodsOnPtr(std::string &out); bool VisitRecordType(clang::RecordType *type) override; diff --git a/cpp2rust/converter/rules/decls.cpp b/cpp2rust/converter/rules/decls.cpp new file mode 100644 index 000000000..cb37c38f8 --- /dev/null +++ b/cpp2rust/converter/rules/decls.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/decls.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace cpp2rust::RuleDecls { + +const clang::Expr *SkipImplicit(const clang::Expr *expr) { + while (true) { + expr = expr->IgnoreParens(); + if (auto full = llvm::dyn_cast(expr)) { + expr = full->getSubExpr(); + } else if (auto temporary = + llvm::dyn_cast(expr)) { + expr = temporary->getSubExpr(); + } else if (auto bind = llvm::dyn_cast(expr)) { + expr = bind->getSubExpr(); + } else if (auto cast = llvm::dyn_cast(expr)) { + expr = cast->getSubExpr(); + } else if (auto construct = llvm::dyn_cast(expr); + construct && construct->isElidable()) { + expr = construct->getArg(0); + } else { + return expr; + } + } +} + +const clang::Expr *GetReturned(const clang::FunctionDecl *rule) { + auto body = llvm::dyn_cast_or_null(rule->getBody()); + assert(body && body->size() == 1 && "rule body must be a single return"); + auto ret = llvm::cast(*body->body_begin()); + auto returned = ret->getRetValue(); + auto construct = + llvm::dyn_cast(SkipImplicit(returned)); + if (construct && construct->getConstructor()->isCopyOrMoveConstructor() && + construct->getConstructor()->isTrivial()) { + return construct->getArg(0); + } + return returned; +} + +clang::QualType EnableIfSameType(const clang::Decl *rule) { + auto tmpl = llvm::dyn_cast(rule); + if (!tmpl) { + return {}; + } + for (auto param : *tmpl->getTemplateParameters()) { + auto type_param = llvm::dyn_cast(param); + if (!type_param || type_param->getIdentifier() || + !type_param->hasDefaultArgument()) { + continue; + } + auto same = type_param->getDefaultArgument() + .getArgument() + .getAsType() + ->getAs(); + if (!same || !same->isTypeAlias() || + same->getTemplateName().getAsTemplateDecl()->getName() != "SameAs") { + continue; + } + return same->template_arguments()[1].getAsType(); + } + return {}; +} + +std::optional> +DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params, + clang::QualType pattern, clang::QualType actual) { + Trial trial(sema); + llvm::SmallVector deduced(params->size()); + clang::sema::TemplateDeductionInfo info(params->getTemplateLoc()); + if (sema.DeduceTemplateArguments(params, clang::TemplateArgument(pattern), + clang::TemplateArgument(actual), info, + deduced, /*NumberOfArgumentsMustMatch=*/ + true) != + clang::TemplateDeductionResult::Success || + trial.failed()) { + return std::nullopt; + } + return std::vector(deduced.begin(), deduced.end()); +} + +} // namespace cpp2rust::RuleDecls diff --git a/cpp2rust/converter/rules/decls.h b/cpp2rust/converter/rules/decls.h new file mode 100644 index 000000000..7169a72e2 --- /dev/null +++ b/cpp2rust/converter/rules/decls.h @@ -0,0 +1,47 @@ +#pragma once + +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace cpp2rust::RuleDecls { +class Trial { +public: + explicit Trial(clang::Sema &sema) + : diagnostics_(sema.getDiagnostics()), + suppressed_(diagnostics_.getSuppressAllDiagnostics()), + trap_(sema, /*ForValidityCheck=*/true) { + diagnostics_.setSuppressAllDiagnostics(true); + } + + ~Trial() { diagnostics_.setSuppressAllDiagnostics(suppressed_); } + + bool failed() const { return trap_.hasErrorOccurred(); } + +private: + clang::DiagnosticsEngine &diagnostics_; + bool suppressed_; + clang::Sema::SFINAETrap trap_; +}; + +const clang::Expr *SkipImplicit(const clang::Expr *expr); + +const clang::Expr *GetReturned(const clang::FunctionDecl *rule); + +clang::QualType EnableIfSameType(const clang::Decl *rule); + +std::optional> +DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params, + clang::QualType pattern, clang::QualType actual); + +} // namespace cpp2rust::RuleDecls diff --git a/cpp2rust/converter/rules/expr_matcher.cpp b/cpp2rust/converter/rules/expr_matcher.cpp new file mode 100644 index 000000000..01e37e6dc --- /dev/null +++ b/cpp2rust/converter/rules/expr_matcher.cpp @@ -0,0 +1,378 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/expr_matcher.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "converter/rules/decls.h" + +namespace cpp2rust::AstMatcher { + +namespace { + +bool hasSameArgTypes(clang::ASTContext &ctx, const clang::Expr *use, + const clang::Expr *returned) { + auto use_call = llvm::dyn_cast(RuleDecls::SkipImplicit(use)); + auto rule_call = + llvm::dyn_cast(RuleDecls::SkipImplicit(returned)); + if (!use_call || !rule_call) { + return true; + } + if (use_call->getNumArgs() != rule_call->getNumArgs()) { + return false; + } + for (unsigned i = 0; i < use_call->getNumArgs(); ++i) { + if (!ctx.hasSameUnqualifiedType(use_call->getArg(i)->getType(), + rule_call->getArg(i)->getType())) { + return false; + } + } + return true; +} + +struct MacroUsage { + std::string name; + + bool operator==(const MacroUsage &) const = default; +}; + +struct RewrittenOperatorUsage { + const clang::Decl *decl; + clang::BinaryOperatorKind op; + + bool operator==(const RewrittenOperatorUsage &) const = default; +}; + +using ResolvedDecl = std::variant; + +ResolvedDecl getResolvedDecl(clang::ASTContext &ctx, const clang::Expr *expr, + bool must_resolve) { + expr = RuleDecls::SkipImplicit(expr); + if (llvm::isa(expr) && + expr->getBeginLoc().isMacroID()) { + return MacroUsage{ + clang::Lexer::getImmediateMacroName( + expr->getBeginLoc(), ctx.getSourceManager(), ctx.getLangOpts()) + .str()}; + } + if (auto rewritten = + llvm::dyn_cast(expr)) { + auto inner = getResolvedDecl(ctx, rewritten->getDecomposedForm().InnerBinOp, + must_resolve); + assert(std::holds_alternative(inner) && + "rewritten operator does not call a declaration"); + return RewrittenOperatorUsage{std::get(inner), + rewritten->getOperator()}; + } + if (auto cast = llvm::dyn_cast(expr)) { + return getResolvedDecl(ctx, cast->getSubExpr(), must_resolve); + } + if (auto call = llvm::dyn_cast(expr)) { + if (auto callee = call->getDirectCallee()) { + return callee->getCanonicalDecl(); + } + } else if (auto construct = llvm::dyn_cast(expr)) { + return construct->getConstructor()->getCanonicalDecl(); + } else if (auto member = llvm::dyn_cast(expr)) { + return member->getMemberDecl()->getCanonicalDecl(); + } else if (auto ref = llvm::dyn_cast(expr)) { + return ref->getDecl()->getCanonicalDecl(); + } + if (must_resolve) { + expr->dump(llvm::errs(), ctx); + } + assert(!must_resolve && "unhandled declaration in rule body"); + return std::monostate(); +} + +// Receivers are references in the rules, dereference the pointer to get a +// reference. +clang::Expr *getObject(clang::ASTContext &ctx, clang::Expr *object) { + auto type = object->getType(); + if (type->isPointerType()) { + type = type->getPointeeType(); + } else if (object->isLValue()) { + return object; + } + return new (ctx) + clang::OpaqueValueExpr(object->getBeginLoc(), type, clang::VK_LValue); +} + +std::vector getOperands(clang::ASTContext &ctx, + const clang::Expr *use) { + auto expr = const_cast(RuleDecls::SkipImplicit(use)); + while (auto cast = llvm::dyn_cast(expr)) { + expr = + const_cast(RuleDecls::SkipImplicit(cast->getSubExpr())); + } + std::vector operands; + if (auto rewritten = + llvm::dyn_cast(expr)) { + auto decomposed = rewritten->getDecomposedForm(); + operands = {const_cast(decomposed.LHS), + const_cast(decomposed.RHS)}; + } else if (auto op = llvm::dyn_cast(expr)) { + operands.assign(op->arg_begin(), op->arg_end()); + if (!operands.empty() && + llvm::isa_and_nonnull(op->getDirectCallee())) { + operands[0] = operands[0]->IgnoreParenImpCasts(); + } + } else if (auto call = llvm::dyn_cast(expr)) { + operands.push_back(getObject( + ctx, call->getImplicitObjectArgument()->IgnoreParenImpCasts())); + operands.insert(operands.end(), call->arg_begin(), call->arg_end()); + } else if (auto call = llvm::dyn_cast(expr)) { + operands.insert(operands.end(), call->arg_begin(), call->arg_end()); + } else if (auto construct = llvm::dyn_cast(expr)) { + operands.assign(construct->arg_begin(), construct->arg_end()); + } else if (auto member = llvm::dyn_cast(expr)) { + auto base = member->getBase()->IgnoreParenImpCasts(); + if (auto arrow = llvm::dyn_cast(base); + arrow && arrow->getOperator() == clang::OO_Arrow) { + base = arrow->getArg(0); + } + operands.push_back(getObject(ctx, base)); + } + while (!operands.empty() && + llvm::isa(operands.back())) { + operands.pop_back(); + } + for (auto &operand : operands) { + auto literal = llvm::dyn_cast(operand); + if (!literal) { + continue; + } + auto record = literal->getType()->getAsRecordDecl(); + if (!record || !record->hasAttr()) { + continue; + } + auto init = llvm::cast(literal->getInitializer()); + assert(init->getNumInits() == 1); + operand = init->getInit(0); + } + return operands; +} + +clang::FunctionDecl *deduce(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + clang::TemplateArgumentListInfo &explicit_args, + llvm::ArrayRef operands) { + RuleDecls::Trial trial(sema); + clang::FunctionDecl *specialization = nullptr; + clang::sema::TemplateDeductionInfo info(rule->getLocation()); + auto result = sema.DeduceTemplateArguments( + rule, explicit_args.size() ? &explicit_args : nullptr, operands, + specialization, info, false, false, false, clang::QualType(), + clang::Expr::Classification(), false, + [](llvm::ArrayRef, bool) { return false; }); + return result == clang::TemplateDeductionResult::Success && !trial.failed() + ? specialization + : nullptr; +} + +const clang::Expr * +substituteSpecializationInRuleTmpl(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + clang::FunctionDecl *specialization) { + if (!specialization) { + return nullptr; + } + RuleDecls::Trial trial(sema); + auto pattern = rule->getTemplatedDecl(); + clang::EnterExpressionEvaluationContext unevaluated( + sema, clang::Sema::ExpressionEvaluationContext::Unevaluated); + clang::Sema::InstantiatingTemplate instantiating(sema, rule->getLocation(), + specialization); + clang::Sema::ContextRAII context(sema, specialization); + clang::LocalInstantiationScope scope(sema); + for (unsigned i = 0, j = 0; i < pattern->getNumParams(); ++i) { + auto param = pattern->getParamDecl(i); + if (!param->isParameterPack()) { + scope.InstantiatedLocal(param, specialization->getParamDecl(j++)); + continue; + } + scope.MakeInstantiatedLocalArgPack(param); + for (; j < specialization->getNumParams(); ++j) { + scope.InstantiatedLocalPackArg(param, specialization->getParamDecl(j)); + } + } + auto result = + sema.SubstExpr(const_cast(RuleDecls::GetReturned(pattern)), + sema.getTemplateInstantiationArgs(specialization)); + return result.isInvalid() || trial.failed() ? nullptr : result.get(); +} + +clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + const clang::Expr *use) { + auto loc = rule->getLocation(); + clang::TemplateArgumentListInfo explicit_args(loc, loc); + llvm::SmallBitVector deducible; + sema.MarkDeducedTemplateParameters(rule, deducible); + if (deducible.all()) { + return explicit_args; + } + auto params = rule->getTemplateParameters(); + auto deduced = RuleDecls::DeduceFromType( + sema, params, rule->getTemplatedDecl()->getReturnType(), + RuleDecls::SkipImplicit(use) + ->getType() + .getCanonicalType() + .getUnqualifiedType()); + if (!deduced) { + return explicit_args; + } + auto last = static_cast(deducible.find_last_unset()); + for (unsigned i = 0; i <= last; ++i) { + auto param = params->getParam(i); + if (param->isParameterPack() || (*deduced)[i].isNull()) { + break; + } + clang::QualType nttp_type; + if (auto nttp = llvm::dyn_cast(param)) { + nttp_type = nttp->getType(); + } + explicit_args.addArgument( + sema.getTrivialTemplateArgumentLoc((*deduced)[i], nttp_type, loc)); + } + return explicit_args; +} + +void checkCallable(clang::Sema &sema, const clang::Expr *use, + clang::FunctionDecl *rule) { + auto ref = llvm::dyn_cast(RuleDecls::SkipImplicit(use)); + if (ref && llvm::isa(ref->getDecl())) { + return; + } + RuleDecls::Trial trial(sema); + auto loc = use->getBeginLoc(); + auto operands = getOperands(sema.Context, use); + clang::OverloadCandidateSet set(loc, clang::OverloadCandidateSet::CSK_Normal); + auto found = clang::DeclAccessPair::make(rule, clang::AS_public); + if (auto tmpl = rule->getDescribedFunctionTemplate()) { + auto explicit_args = getResultArgs(sema, tmpl, use); + sema.AddTemplateOverloadCandidate( + tmpl, found, explicit_args.size() ? &explicit_args : nullptr, operands, + set); + } else { + sema.AddOverloadCandidate(rule, found, operands, set); + } + clang::OverloadCandidateSet::iterator best; + if (set.BestViableFunction(sema, loc, best) != clang::OR_Success) { + use->dump(llvm::errs(), sema.Context); + rule->dump(llvm::errs()); + llvm::report_fatal_error("matched rule is not callable with the use"); + } +} + +using MatchResult = std::optional>; + +MatchResult matchVar(clang::ASTContext &ctx, const ResolvedDecl &usage_decl, + const clang::VarDecl *rule) { + if (getResolvedDecl(ctx, rule->getInit(), /*must_resolve=*/true) != + usage_decl) { + return std::nullopt; + } + return llvm::ArrayRef(); +} + +MatchResult matchFunction(clang::Sema &sema, const clang::Expr *use, + const ResolvedDecl &usage_decl, + clang::FunctionDecl *rule) { + auto &ctx = sema.Context; + auto returned = RuleDecls::GetReturned(rule); + if (getResolvedDecl(ctx, returned, /*must_resolve=*/true) != usage_decl || + !hasSameArgTypes(ctx, use, returned)) { + return std::nullopt; + } + checkCallable(sema, use, rule); + return llvm::ArrayRef(); +} + +MatchResult matchTemplate(clang::Sema &sema, const clang::Expr *use, + const ResolvedDecl &usage_decl, + clang::FunctionTemplateDecl *rule) { + auto &ctx = sema.Context; + auto explicit_args = getResultArgs(sema, rule, use); + auto specialization = + deduce(sema, rule, explicit_args, getOperands(ctx, use)); + auto returned = + substituteSpecializationInRuleTmpl(sema, rule, specialization); + if (!returned || + getResolvedDecl(ctx, returned, /*must_resolve=*/true) != usage_decl) { + return std::nullopt; + } + checkCallable(sema, use, rule->getTemplatedDecl()); + return specialization->getTemplateSpecializationArgs()->asArray(); +} + +} // namespace + +MatchResult Match(clang::Sema &sema, const clang::Expr *use, + const clang::NamedDecl *rule_decl) { + if (use->isInstantiationDependent()) { + return std::nullopt; + } + auto usage_decl = getResolvedDecl(sema.Context, use, /*must_resolve=*/false); + if (std::holds_alternative(usage_decl)) { + return std::nullopt; + } + if (auto var = llvm::dyn_cast(rule_decl)) { + return matchVar(sema.Context, usage_decl, var); + } + auto rule = const_cast(rule_decl)->getAsFunction(); + if (auto tmpl = rule->getDescribedFunctionTemplate()) { + return matchTemplate(sema, use, usage_decl, tmpl); + } + return matchFunction(sema, use, usage_decl, rule); +} + +const clang::NamedDecl *MoreSpecialized(clang::Sema &sema, + const clang::Expr *use, + const clang::NamedDecl *a, + const clang::NamedDecl *b) { + auto a_function = a->getAsFunction(); + auto b_function = b->getAsFunction(); + auto a_tmpl = + a_function ? a_function->getDescribedFunctionTemplate() : nullptr; + auto b_tmpl = + b_function ? b_function->getDescribedFunctionTemplate() : nullptr; + if (!a_tmpl || !b_tmpl) { + if (!a_tmpl && b_tmpl) { + return a; + } + return a_tmpl && !b_tmpl ? b : nullptr; + } + RuleDecls::Trial trial(sema); + auto better = sema.getMoreSpecializedTemplate( + a_tmpl, b_tmpl, use->getBeginLoc(), clang::TPOC_Call, + getOperands(sema.Context, use).size()); + if (!better) { + better = sema.getMoreSpecializedTemplate(a_tmpl, b_tmpl, use->getBeginLoc(), + clang::TPOC_Conversion, 0); + } + if (better == a_tmpl) { + return a; + } + return better == b_tmpl ? b : nullptr; +} + +} // namespace cpp2rust::AstMatcher diff --git a/cpp2rust/converter/rules/expr_matcher.h b/cpp2rust/converter/rules/expr_matcher.h new file mode 100644 index 000000000..a0555f0ab --- /dev/null +++ b/cpp2rust/converter/rules/expr_matcher.h @@ -0,0 +1,23 @@ +#pragma once + +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include + +#include + +namespace cpp2rust::AstMatcher { +std::optional> +Match(clang::Sema &sema, const clang::Expr *use, + const clang::NamedDecl *rule_decl); + +const clang::NamedDecl *MoreSpecialized(clang::Sema &sema, + const clang::Expr *use, + const clang::NamedDecl *a, + const clang::NamedDecl *b); +} // namespace cpp2rust::AstMatcher diff --git a/cpp2rust/converter/rules/matcher.cpp b/cpp2rust/converter/rules/matcher.cpp index 1dd53121f..7d79dcedb 100644 --- a/cpp2rust/converter/rules/matcher.cpp +++ b/cpp2rust/converter/rules/matcher.cpp @@ -6,16 +6,15 @@ #include #include +#include "converter/converter.h" + namespace cpp2rust::Matcher { -Bindings MapBindings(const Bindings &bindings) { - Bindings mapped(bindings.size()); - for (unsigned i = 0; i < bindings.size(); ++i) { - if (bindings[i]) { - mapped[i] = MapBinding(bindings, i); - } - } - return mapped; +std::string MapBinding(Converter &converter, const Bindings &bindings, + unsigned n) { + auto type = bindings.at(n); + assert(!type.isNull() && "template parameter is not bound to a type"); + return converter.GetUnboxedTypeAsString(type); } // Substitutes concrete types into a target template string using the provided @@ -23,10 +22,10 @@ Bindings MapBindings(const Bindings &bindings) { // corresponding instantiated type from `types`. // // Example: -// types = { {"i32"} } +// types = { int } // tgt_template = "Vec" // result = "Vec" -std::string InstantiateTgt(const Bindings &types, +std::string InstantiateTgt(Converter &converter, const Bindings &types, const std::string &tgt_template) { assert(types.size() <= TranslationRule::kMaxGenerics && "template placeholder exceeds kMaxGenerics"); @@ -40,7 +39,8 @@ std::string InstantiateTgt(const Bindings &types, ++pos; continue; } - const auto &repl = types.at(instantiated_template[pos + 1] - '1').value(); + auto repl = + MapBinding(converter, types, instantiated_template[pos + 1] - '1'); instantiated_template.replace(pos, 2, repl); pos += repl.length(); } diff --git a/cpp2rust/converter/rules/matcher.h b/cpp2rust/converter/rules/matcher.h index f5f9e0db4..9730e8e0c 100644 --- a/cpp2rust/converter/rules/matcher.h +++ b/cpp2rust/converter/rules/matcher.h @@ -8,31 +8,24 @@ #include #include -#include #include #include #include #include "converter/translation_rule.h" +namespace cpp2rust { +class Converter; +} + namespace cpp2rust::Matcher { -using Bindings = std::vector>; +using Bindings = std::vector; template using Match = std::pair; -std::string Key(const TranslationRule::ExprRule &rule); -std::string Key(const TranslationRule::TypeRule &rule); - -Match Find(clang::ASTContext &ctx, - const clang::Expr *expr); -Match Find(clang::ASTContext &ctx, - clang::QualType type); - -bool HasRuleNamed(clang::ASTContext &ctx, const clang::FunctionDecl *decl); - -std::string MapBinding(const Bindings &bindings, unsigned n); -Bindings MapBindings(const Bindings &bindings); +std::string MapBinding(Converter &converter, const Bindings &bindings, + unsigned n); -std::string InstantiateTgt(const Bindings &types, +std::string InstantiateTgt(Converter &converter, const Bindings &types, const std::string &tgt_template); } // namespace cpp2rust::Matcher diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index e2c370d54..d3a1ed583 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -3,62 +3,275 @@ #include "converter/rules/registry.h" +#include +#include #include +#include +#include +#include +#include #include #include #include +#include +#include +#include +#include #include +#include #include "converter/converter_lib.h" -#include "converter/printer.h" +#include "converter/rules/expr_matcher.h" +#include "converter/rules/rule_keys.h" +#include "converter/rules/rules_loader.h" +#include "converter/rules/type_matcher.h" namespace cpp2rust::RuleRegistry { namespace { Model model_ = Model::kUnsafe; -bool translation_rules_loaded_ = false; - -ExprRuleMap exprs_; // key -> ExprRule -TypeRuleMap types_; // key -> TypeRule - -void AddTypeRule(std::string src, TranslationRule::TypeRule &&rule) { - rule.src = std::move(src); - auto key = Matcher::Key(rule); - types_.emplace(std::move(key), std::move(rule)); -} - -void addRulesFromDirectory(const std::filesystem::path &dir, Model model) { - namespace fs = std::filesystem; - for (const auto &entry : fs::directory_iterator(dir)) { - const auto &path = entry.path(); - assert(fs::exists(path / "ir_src.json") && - (fs::exists(path / "ir_unsafe.json") || - fs::exists(path / "ir_refcount.json"))); - auto [expr_rules, type_rules] = TranslationRule::Load(path, model); - if (expr_rules.empty() && type_rules.empty()) { - log() << "No rules found in " << path << '\n'; +clang::Sema *sema_ = nullptr; + +std::unordered_multimap exprs_; +std::unordered_multimap types_; + +const llvm::json::Object *findRule(Model model, const std::string &dir, + const std::string &name) { + static std::unordered_map indexes; + auto path = + std::filesystem::path(RULES_IR_DIR) / dir / + (model == Model::kRefCount ? "ir_refcount.json" : "ir_unsafe.json"); + auto [it, inserted] = indexes.try_emplace(path.string()); + if (inserted && std::filesystem::exists(path)) { + it->second = RulesLoader::ReadIndex(path); + } + return it->second.getObject(name); +} + +bool pointsToUserType(clang::QualType type) { + if (!type->isPointerType()) { + return false; + } + auto tag = type->getPointeeType()->getAsTagDecl(); + return tag && IsUserDefinedDecl(tag); +} + +bool isRuleName(llvm::StringRef name) { + return name.size() > 1 && (name[0] == 'f' || name[0] == 't') && + llvm::all_of(name.drop_front(), llvm::isDigit); +} + +// The T of a rule's trailing `Init &&...args` pack. +clang::QualType getInitPattern(const clang::FunctionDecl *rule) { + if (rule->param_empty()) { + return {}; + } + auto expansion = + rule->parameters().back()->getType()->getAs(); + if (!expansion) { + return {}; + } + auto alias = expansion->getPattern() + .getNonReferenceType() + ->getAs(); + if (!alias || !alias->isTypeAlias() || + alias->getTemplateName().getAsTemplateDecl()->getName() != "Init") { + return {}; + } + return alias->template_arguments()[0].getAsType(); +} + +void checkGenerics(const std::string &dir, const std::string &name, + const clang::FunctionDecl *decl, + const TranslationRule::ExprRule &rule) { + std::unordered_set declared; + if (auto tmpl = decl->getDescribedFunctionTemplate()) { + for (auto param : *tmpl->getTemplateParameters()) { + declared.insert(param->getName().str()); + } + } + for (unsigned i = 1; i <= rule.generics.size(); ++i) { + if (!declared.contains(std::format("T{}", i))) { + llvm::errs() << dir << "::" << name << ": generic T" << i + << " declared but missing from src\n"; + llvm::report_fatal_error("Absent generic from src"); + } + } + if (rule.usesInit() && getInitPattern(decl).isNull()) { + llvm::errs() << dir << "::" << name << '\n'; + llvm::report_fatal_error("Expr rule uses init but its src pack is not " + "declared as Init"); + } +} + +void addRule(clang::ASTContext &ctx, const std::string &dir, + const std::string &name, const clang::NamedDecl *decl) { + auto rule = findRule(Model::kUnsafe, dir, name); + if (model_ == Model::kRefCount) { + if (auto refcount_rule = findRule(model_, dir, name)) { + rule = refcount_rule; + } + } + assert(rule && "rule has no target"); + if (!name.starts_with('f')) { + auto keys = RuleKeys::GetTypeKeys(decl); + assert(!keys.empty() && "type rule has no key"); + for (auto key : keys) { + types_.emplace( + key, TypeRuleDecl{decl, TranslationRule::ParseTypeRuleJSON(*rule)}); + } + return; + } + auto expr_rule = TranslationRule::ParseExprRuleJSON(*rule); + if (auto function = decl->getAsFunction()) { + checkGenerics(dir, name, function, expr_rule); + } else { + assert(llvm::isa(decl) && + "expression rule is neither a function nor a variable"); + } + auto keys = RuleKeys::GetExprKeys(ctx, decl); + assert(!keys.empty() && "expression rule has no key"); + for (const auto &key : keys) { + exprs_.emplace( + key, ExprRuleDecl{decl, TranslationRule::ParseExprRuleJSON(*rule)}); + } +} + +void addCxxRules(clang::ASTContext &ctx, const clang::NamespaceDecl *rules) { + for (auto child : rules->decls()) { + auto ns = llvm::dyn_cast(child); + if (!ns) { continue; } - for (auto &[_, rule] : expr_rules) { - exprs_.emplace(Matcher::Key(rule), std::move(rule)); + auto dir = ns->getName(); + bool is_rule_dir = dir.consume_front("cpp2rust_rules_"); + if (!is_rule_dir) { + llvm::report_fatal_error("rule namespace is not named after a rule dir"); + } + for (auto decl : ns->decls()) { + auto named = llvm::cast(decl); + if (!isRuleName(named->getName())) { + llvm::report_fatal_error("declaration is not a rule"); + } + addRule(ctx, dir.str(), named->getName().str(), named); } - for (auto &[_, rule] : type_rules) { - auto key = Matcher::Key(rule); - auto [begin, end] = types_.equal_range(key); - for (auto it = begin; it != end; ++it) { - if (it->second.src == rule.src) { - llvm::errs() << "ERROR: duplicate type rule for C++ type '" - << rule.src << "': maps to both '" - << it->second.type_info.type << "' and '" - << rule.type_info.type << "'\n"; - std::exit(EXIT_FAILURE); + } +} + +void addCRule(clang::ASTContext &ctx, const clang::NamedDecl *decl) { + auto identifier = decl->getName(); + if (!identifier.consume_front("cpp2rust_rules_")) { + return; + } + auto [dir, name] = identifier.rsplit('_'); + if (!isRuleName(name)) { + llvm::report_fatal_error("declaration is not a rule"); + } + addRule(ctx, dir.str(), name.str(), decl); +} + +struct ExprMatch { + ExprRuleDecl *rule_decl = nullptr; + llvm::ArrayRef args; +}; + +ExprMatch findExpr(const clang::Expr *use) { + ExprMatch best; + auto consider = [&](const RuleKeys::ExprKey &key) { + auto [begin, end] = exprs_.equal_range(key); + for (auto it = begin; it != end; ++it) { + auto args = AstMatcher::Match(*sema_, use, it->second.decl); + if (!args) { + continue; + } + if (best.rule_decl) { + auto better = AstMatcher::MoreSpecialized(*sema_, use, it->second.decl, + best.rule_decl->decl); + if (!better) { + use->dump(llvm::errs(), sema_->Context); + it->second.decl->dump(llvm::errs()); + best.rule_decl->decl->dump(llvm::errs()); + llvm::report_fatal_error( + "use matches several rules, none more specialized"); + } + if (better != it->second.decl) { + continue; } } - types_.emplace(std::move(key), std::move(rule)); + best = {&it->second, *args}; + } + }; + auto key = RuleKeys::GetExprKey(sema_->Context, use); + if (!key) { + return best; + } + consider(*key); + if (auto member = std::get_if(&*key)) { + consider(RuleKeys::Unqualified{member->name}); + } + return best; +} + +struct TypeRuleMatch { + TypeRuleDecl *rule_decl = nullptr; + AstMatcher::TypeMatch match; +}; + +TypeRuleMatch findType(clang::QualType use) { + TypeRuleMatch best; + auto consider = [&](const RuleKeys::TypeKey &key) { + auto [begin, end] = types_.equal_range(key); + for (auto it = begin; it != end; ++it) { + auto match = AstMatcher::MatchType(*sema_, use, it->second.decl); + if (!match) { + continue; + } + if (best.rule_decl) { + if (match->depth > best.match.depth) { + continue; + } + if (match->depth == best.match.depth) { + auto better = AstMatcher::MoreSpecializedType(*sema_, it->second.decl, + best.rule_decl->decl); + if (!better) { + llvm::report_fatal_error( + "type matches several rules, none more specialized"); + } + if (better != it->second.decl) { + continue; + } + } + } + best = {&it->second, *std::move(match)}; + } + }; + for (auto key : RuleKeys::GetTypeKeys(sema_->Context, use)) { + consider(key); + } + return best; +} + +Matcher::Bindings getBindings(const clang::NamedDecl *rule, + llvm::ArrayRef args) { + Matcher::Bindings bindings; + auto tmpl = llvm::dyn_cast(rule); + if (!tmpl) { + return bindings; + } + auto params = tmpl->getTemplateParameters(); + for (unsigned i = 0; i < params->size() && i < args.size(); ++i) { + auto name = params->getParam(i)->getName(); + unsigned n = 0; + if (!name.consume_front("T") || name.getAsInteger(10, n) || + args[i].getKind() != clang::TemplateArgument::Type) { + continue; } + bindings.resize(std::max(bindings.size(), n)); + bindings[n - 1] = args[i].getAsType(); } + return bindings; } const TranslationRule::TypeInfo & @@ -70,29 +283,69 @@ GetParamInfo(clang::ASTContext &ctx, const clang::Expr *expr, unsigned index) { } // namespace -std::ranges::subrange -ExprCandidates(const std::string &key) { - auto [begin, end] = exprs_.equal_range(key); - return {begin, end}; -} - -std::ranges::subrange -TypeCandidates(const std::string &key) { - auto [begin, end] = types_.equal_range(key); - return {begin, end}; +void Load(clang::Sema &sema, Model model) { + auto &ctx = sema.Context; + sema_ = &sema; + model_ = model; + exprs_.clear(); + types_.clear(); + for (auto decl : ctx.getTranslationUnitDecl()->decls()) { + auto named = llvm::dyn_cast(decl); + if (!named || !named->getIdentifier()) { + continue; + } + if (!ctx.getLangOpts().CPlusPlus) { + addCRule(ctx, named); + } else if (auto ns = llvm::dyn_cast(named); + ns && ns->getName() == "cpp2rust_rules") { + addCxxRules(ctx, ns); + } + } } Matcher::Match Search(clang::ASTContext &ctx, const clang::Expr *expr) { - if (RefersToUserDefinedDecl(expr)) { + if (ExprHasUserDefinedDecl(expr)) { + return {}; + } + auto match = findExpr(expr); + if (!match.rule_decl) { return {}; } - return Matcher::Find(ctx, expr); + return {&match.rule_decl->rule, + getBindings(match.rule_decl->decl, match.args)}; +} + +clang::QualType GetInitType(const clang::Expr *expr) { + auto match = findExpr(expr); + assert(match.rule_decl && "expression must have a translation rule"); + auto function = match.rule_decl->decl->getAsFunction(); + auto pattern = getInitPattern(function); + assert(!pattern.isNull() && "rule has no Init pack"); + clang::MultiLevelTemplateArgumentList args( + function->getDescribedFunctionTemplate(), match.args, /*Final=*/false); + return sema_->SubstType(pattern, args, expr->getBeginLoc(), + clang::DeclarationName()); +} + +bool HasExprRule(const clang::FunctionDecl *decl) { + auto key = RuleKeys::GetDeclKey(decl); + if (!key) { + return false; + } + auto member = std::get_if(&*key); + return exprs_.contains(*key) || + (member && exprs_.contains(RuleKeys::Unqualified{member->name})); } Matcher::Match Search(clang::ASTContext &ctx, clang::QualType qual_type) { - return Matcher::Find(ctx, qual_type); + auto match = findType(qual_type); + if (!match.rule_decl) { + return {}; + } + return {&match.rule_decl->rule, + getBindings(match.rule_decl->decl, match.match.args)}; } const TranslationRule::ExprRule *GetExprRule(clang::ASTContext &ctx, @@ -102,25 +355,18 @@ const TranslationRule::ExprRule *GetExprRule(clang::ASTContext &ctx, bool MapsToPointer(clang::ASTContext &ctx, clang::QualType qual_type) { auto rule = Search(ctx, qual_type).first; - return rule && rule->type_info.is_pointer(); + if (!rule) { + return pointsToUserType(qual_type); + } + return rule->type_info.is_pointer(); } bool MapsToRefcountPointer(clang::ASTContext &ctx, clang::QualType qual_type) { auto rule = Search(ctx, qual_type).first; - return rule && rule->type_info.is_refcount_pointer; -} - -const std::vector *MappedDerives(clang::ASTContext &ctx, - clang::QualType qual_type) { - auto rule = Search(ctx, qual_type).first; - return rule ? &rule->type_info.derives : nullptr; -} - -void SetDerives(clang::ASTContext &ctx, clang::QualType qual_type, - std::vector derives) { - if (auto *rule = Search(ctx, qual_type).first) { - rule->type_info.derives = std::move(derives); + if (!rule) { + return model_ == Model::kRefCount && pointsToUserType(qual_type); } + return rule->type_info.is_refcount_pointer; } bool ReturnsPointer(clang::ASTContext &ctx, const clang::Expr *expr) { @@ -128,6 +374,16 @@ bool ReturnsPointer(clang::ASTContext &ctx, const clang::Expr *expr) { return rule && rule->return_type.is_pointer(); } +bool ReturnsReference(const clang::Expr *expr) { + if (ExprHasUserDefinedDecl(expr)) { + return false; + } + auto match = findExpr(expr); + return match.rule_decl && match.rule_decl->decl->getAsFunction() + ->getReturnType() + ->isReferenceType(); +} + bool ParamIsPointer(clang::ASTContext &ctx, const clang::Expr *expr, unsigned index) { return GetParamInfo(ctx, expr, index).is_pointer(); @@ -151,70 +407,4 @@ bool IsLibcPassthrough(clang::ASTContext &ctx, const clang::Expr *expr) { Model CurrentModel() { return model_; } -void AddRuleForUserDefinedType(clang::ASTContext &ctx, clang::NamedDecl *decl) { - auto cpp_name = Printer::ToString(ctx, GetTypeForDecl(ctx, decl)); - auto rs_name = Printer::ToRustName(cpp_name); - - AddTypeRule(cpp_name, TranslationRule::TypeRule::Plain(rs_name)); - - if (auto record_decl = llvm::dyn_cast(decl)) { - // Forward declaration - if (!record_decl->isThisDeclarationADefinition()) { - return; - } - - if (auto cxx_decl = llvm::dyn_cast(record_decl)) { - if (cxx_decl->isAbstract()) { - switch (model_) { - case Model::kUnsafe: - AddTypeRule(cpp_name + " *", TranslationRule::TypeRule::UnsafePtr( - "*mut dyn " + rs_name)); - break; - case Model::kRefCount: - AddTypeRule(cpp_name + " *", TranslationRule::TypeRule::RefcountPtr( - "PtrDyn')); - break; - } - } else { - switch (model_) { - case Model::kUnsafe: - AddTypeRule(cpp_name + " *", - TranslationRule::TypeRule::UnsafePtr("*mut " + rs_name)); - break; - case Model::kRefCount: - AddTypeRule(cpp_name + " *", TranslationRule::TypeRule::RefcountPtr( - "Ptr<" + rs_name + '>')); - break; - } - } - - for (auto *nested : GetNestedStructs(cxx_decl)) { - AddRuleForUserDefinedType(ctx, nested); - } - } - } -} - -void Load(Model model, const std::string &rules_dir) { - model_ = model; - - if (translation_rules_loaded_) { - return; - } - translation_rules_loaded_ = true; - - addRulesFromDirectory(rules_dir, model); - -#if 0 - for (auto &[src, rule] : exprs_) { - log() << "Expr key: " << src << '\n'; - rule.dump(); - } - for (auto &[src, rule] : types_) { - log() << "Type key: " << src << '\n'; - rule.dump(); - } -#endif -} - } // namespace cpp2rust::RuleRegistry diff --git a/cpp2rust/converter/rules/registry.h b/cpp2rust/converter/rules/registry.h index aaa65d03b..8de311400 100644 --- a/cpp2rust/converter/rules/registry.h +++ b/cpp2rust/converter/rules/registry.h @@ -7,8 +7,8 @@ #include #include #include +#include -#include #include #include #include @@ -18,15 +18,17 @@ #include "converter/translation_rule.h" namespace cpp2rust::RuleRegistry { -using ExprRuleMap = - std::unordered_multimap; -using TypeRuleMap = - std::unordered_multimap; +struct ExprRuleDecl { + const clang::NamedDecl *decl; + TranslationRule::ExprRule rule; +}; -std::ranges::subrange -ExprCandidates(const std::string &key); -std::ranges::subrange -TypeCandidates(const std::string &key); +struct TypeRuleDecl { + const clang::NamedDecl *decl; + TranslationRule::TypeRule rule; +}; + +void Load(clang::Sema &sema, Model model); Matcher::Match Search(clang::ASTContext &ctx, const clang::Expr *expr); @@ -35,19 +37,16 @@ Matcher::Match Search(clang::ASTContext &ctx, const TranslationRule::ExprRule *GetExprRule(clang::ASTContext &ctx, const clang::Expr *expr); +clang::QualType GetInitType(const clang::Expr *expr); +bool HasExprRule(const clang::FunctionDecl *decl); bool IsLibcPassthrough(clang::ASTContext &ctx, const clang::Expr *expr); bool ReturnsPointer(clang::ASTContext &ctx, const clang::Expr *expr); +bool ReturnsReference(const clang::Expr *expr); bool ParamIsPointer(clang::ASTContext &ctx, const clang::Expr *expr, unsigned index); bool MapsToPointer(clang::ASTContext &ctx, clang::QualType qual_type); bool MapsToRefcountPointer(clang::ASTContext &ctx, clang::QualType qual_type); -const std::vector *MappedDerives(clang::ASTContext &ctx, - clang::QualType qual_type); -void SetDerives(clang::ASTContext &ctx, clang::QualType qual_type, - std::vector derives); Model CurrentModel(); -void Load(Model model, const std::string &rules_dir); -void AddRuleForUserDefinedType(clang::ASTContext &ctx, clang::NamedDecl *decl); } // namespace cpp2rust::RuleRegistry diff --git a/cpp2rust/converter/rules/rule_keys.cpp b/cpp2rust/converter/rules/rule_keys.cpp new file mode 100644 index 000000000..0468187d6 --- /dev/null +++ b/cpp2rust/converter/rules/rule_keys.cpp @@ -0,0 +1,288 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/rule_keys.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "converter/rules/decls.h" + +namespace cpp2rust::RuleKeys { + +namespace { + +const clang::Decl *getParent(clang::QualType type) { + type = type.getNonReferenceType(); + if (type->isPointerType()) { + type = type->getPointeeType(); + } + if (auto spec = type->getAs()) { + if (auto tmpl = llvm::dyn_cast_or_null( + spec->getTemplateName().getAsTemplateDecl())) { + return tmpl->getCanonicalDecl(); + } + return nullptr; + } + auto record = type->getAsCXXRecordDecl(); + if (!record) { + return nullptr; + } + if (auto spec = + llvm::dyn_cast(record)) { + return spec->getSpecializedTemplate()->getCanonicalDecl(); + } + return record->getCanonicalDecl(); +} + +const clang::Decl *getParent(const clang::Decl *decl) { + if (auto record = + llvm::dyn_cast(decl->getDeclContext())) { + return getParent(decl->getASTContext().getCanonicalTagType(record)); + } + return nullptr; +} + +std::vector resolveMemberType(clang::QualType type) { + auto member = type->getAs(); + if (!member) { + return {type}; + } + auto qualifier = member->getQualifier().getAsType(); + assert(qualifier && "member type is not qualified by a type"); + std::vector resolved; + for (auto owner : resolveMemberType(clang::QualType(qualifier, 0))) { + auto spec = owner->getAs(); + auto tmpl = spec ? llvm::dyn_cast_or_null( + spec->getTemplateName().getAsTemplateDecl()) + : nullptr; + assert(tmpl && "member type is not qualified by a class template"); + llvm::SmallVector partials; + tmpl->getPartialSpecializations(partials); + llvm::SmallVector records{tmpl->getTemplatedDecl()}; + records.append(partials.begin(), partials.end()); + for (auto record : records) { + if (!record->hasDefinition()) { + continue; + } + for (auto decl : + record->getDefinition()->lookup(member->getIdentifier())) { + if (auto alias = llvm::dyn_cast(decl)) { + auto types = resolveMemberType(alias->getUnderlyingType()); + resolved.insert(resolved.end(), types.begin(), types.end()); + } + } + } + } + return resolved; +} + +ExprKey getOperatorKey(clang::ASTContext &ctx, + clang::OverloadedOperatorKind op) { + return Unqualified{ctx.DeclarationNames.getCXXOperatorName(op)}; +} + +std::optional getConstructorKey(const clang::Decl *parent) { + if (!parent) { + return std::nullopt; + } + return Constructor{parent}; +} + +std::optional getMemberKey(const clang::Decl *parent, + clang::DeclarationName name) { + if (name.getNameKind() == clang::DeclarationName::CXXConversionFunctionName) { + return getConstructorKey(parent); + } + if (!parent) { + return Unqualified{name}; + } + return Member{parent, name}; +} + +std::optional getDeclKey(const clang::NamedDecl *decl) { + if (auto ctor = llvm::dyn_cast(decl)) { + return getConstructorKey(getParent(ctor)); + } + return getMemberKey(getParent(decl), decl->getDeclName()); +} + +std::optional getExprKey(clang::ASTContext &ctx, + const clang::Expr *expr) { + expr = RuleDecls::SkipImplicit(expr); + if (llvm::isa(expr) && + expr->getBeginLoc().isMacroID()) { + auto macro = clang::Lexer::getImmediateMacroName( + expr->getBeginLoc(), ctx.getSourceManager(), ctx.getLangOpts()); + return Unqualified{clang::DeclarationName(&ctx.Idents.get(macro))}; + } + if (auto rewritten = + llvm::dyn_cast(expr)) { + return getOperatorKey(ctx, clang::BinaryOperator::getOverloadedOperator( + rewritten->getOperator())); + } + if (auto cast = llvm::dyn_cast(expr)) { + return getExprKey(ctx, cast->getSubExpr()); + } + if (auto construct = llvm::dyn_cast(expr)) { + return getDeclKey(construct->getConstructor()); + } + if (auto construct = + llvm::dyn_cast(expr)) { + return getConstructorKey(getParent(construct->getTypeAsWritten())); + } + if (auto call = llvm::dyn_cast(expr)) { + if (auto callee = call->getDirectCallee()) { + return getDeclKey(callee); + } + return getExprKey(ctx, call->getCallee()); + } + if (auto member = llvm::dyn_cast(expr)) { + return getMemberKey(getParent(member->getBaseType()), + member->getMemberName()); + } + if (auto member = llvm::dyn_cast(expr)) { + return getMemberKey(getParent(member->getBaseType()), member->getMember()); + } + if (auto lookup = llvm::dyn_cast(expr)) { + return Unqualified{lookup->getName()}; + } + if (auto ref = llvm::dyn_cast(expr)) { + return Unqualified{ref->getDeclName()}; + } + if (auto member = llvm::dyn_cast(expr)) { + return getDeclKey(member->getMemberDecl()); + } + if (auto ref = llvm::dyn_cast(expr)) { + return getDeclKey(ref->getDecl()); + } + if (auto binary = llvm::dyn_cast(expr)) { + return getOperatorKey( + ctx, clang::BinaryOperator::getOverloadedOperator(binary->getOpcode())); + } + if (auto unary = llvm::dyn_cast(expr)) { + return getOperatorKey( + ctx, clang::UnaryOperator::getOverloadedOperator(unary->getOpcode())); + } + if (llvm::isa(expr)) { + return getOperatorKey(ctx, clang::OO_Subscript); + } + return std::nullopt; +} + +} // namespace + +std::optional GetExprKey(clang::ASTContext &ctx, + const clang::Expr *use) { + return getExprKey(ctx, use); +} + +std::vector GetExprKeys(clang::ASTContext &ctx, + const clang::NamedDecl *rule) { + if (auto var = llvm::dyn_cast(rule)) { + auto key = getExprKey(ctx, var->getInit()); + assert(key && "expression rule has no key"); + return {*key}; + } + auto returned = + RuleDecls::SkipImplicit(RuleDecls::GetReturned(rule->getAsFunction())); + if (auto construct = + llvm::dyn_cast(returned)) { + std::vector keys; + for (auto type : resolveMemberType(construct->getTypeAsWritten())) { + auto key = getConstructorKey(getParent(type)); + assert(key && "expression rule has no key"); + if (!llvm::is_contained(keys, *key)) { + keys.push_back(*key); + } + } + return keys; + } + auto key = getExprKey(ctx, returned); + assert(key && "expression rule has no key"); + return {*key}; +} + +std::optional GetTypeKey(clang::QualType type) { + type = type.getCanonicalType().getUnqualifiedType(); + if (auto parent = getParent(type)) { + return parent; + } + if (auto tag = type->getAsTagDecl()) { + return tag->getCanonicalDecl(); + } + if (type->isDependentType()) { + return std::nullopt; + } + return type.getTypePtr(); +} + +std::vector GetTypeKeys(const clang::NamedDecl *rule) { + auto alias = llvm::dyn_cast(rule); + if (auto tmpl = llvm::dyn_cast(rule)) { + alias = tmpl->getTemplatedDecl(); + } + assert(alias && "type rule is not an alias"); + auto pattern = alias->getUnderlyingType(); + if (pattern->getAs()) { + pattern = RuleDecls::EnableIfSameType(rule); + assert(!pattern.isNull() && + "type rule matching any type must be constrained by enable_if"); + std::vector keys; + for (auto type : resolveMemberType(pattern)) { + auto key = GetTypeKey(type); + assert(key && "type rule has no key"); + if (!llvm::is_contained(keys, *key)) { + keys.push_back(*key); + } + } + return keys; + } + if (auto member = pattern->getAs()) { + auto qualifier = member->getQualifier().getAsType(); + assert(qualifier && "member type is not qualified by a type"); + auto parent = getParent(clang::QualType(qualifier, 0)); + assert(parent && "type rule has no key"); + return {parent}; + } + auto key = GetTypeKey(pattern); + assert(key && "type rule has no key"); + return {*key}; +} + +std::vector GetTypeKeys(clang::ASTContext &ctx, clang::QualType use) { + std::vector keys; + if (auto key = GetTypeKey(use)) { + keys.push_back(*key); + } + for (auto type = use;; type = type.getSingleStepDesugaredType(ctx)) { + if (auto typedef_type = + llvm::dyn_cast(type.getTypePtr())) { + if (auto owner = llvm::dyn_cast( + typedef_type->getDecl()->getDeclContext())) { + auto key = GetTypeKey(ctx.getCanonicalTagType(owner)); + if (key && !llvm::is_contained(keys, *key)) { + keys.push_back(*key); + } + } + } + if (type.getSingleStepDesugaredType(ctx) == type) { + return keys; + } + } +} + +std::optional GetDeclKey(const clang::NamedDecl *decl) { + return getDeclKey(decl); +} + +} // namespace cpp2rust::RuleKeys diff --git a/cpp2rust/converter/rules/rule_keys.h b/cpp2rust/converter/rules/rule_keys.h new file mode 100644 index 000000000..7056a51ed --- /dev/null +++ b/cpp2rust/converter/rules/rule_keys.h @@ -0,0 +1,73 @@ +#pragma once + +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace cpp2rust::RuleKeys { +struct Member { + const clang::Decl *parent; + clang::DeclarationName name; + + bool operator==(const Member &) const = default; +}; + +// Constructors and conversion operators. +struct Constructor { + const clang::Decl *parent; + + bool operator==(const Constructor &) const = default; +}; + +struct Unqualified { + clang::DeclarationName name; + + bool operator==(const Unqualified &) const = default; +}; + +using ExprKey = std::variant; + +using TypeKey = std::variant; + +std::optional GetExprKey(clang::ASTContext &ctx, + const clang::Expr *use); + +std::vector GetExprKeys(clang::ASTContext &ctx, + const clang::NamedDecl *rule); + +std::optional GetDeclKey(const clang::NamedDecl *decl); + +std::optional GetTypeKey(clang::QualType type); + +std::vector GetTypeKeys(const clang::NamedDecl *rule); + +std::vector GetTypeKeys(clang::ASTContext &ctx, clang::QualType use); +} // namespace cpp2rust::RuleKeys + +template <> struct std::hash { + size_t operator()(const cpp2rust::RuleKeys::Member &key) const { + return llvm::hash_combine(key.parent, key.name.getAsOpaquePtr()); + } +}; + +template <> struct std::hash { + size_t operator()(const cpp2rust::RuleKeys::Constructor &key) const { + return llvm::hash_value(key.parent); + } +}; + +template <> struct std::hash { + size_t operator()(const cpp2rust::RuleKeys::Unqualified &key) const { + return llvm::hash_value(key.name.getAsOpaquePtr()); + } +}; diff --git a/cpp2rust/converter/rules/rules_loader.cpp b/cpp2rust/converter/rules/rules_loader.cpp index 529b09601..dada853f8 100644 --- a/cpp2rust/converter/rules/rules_loader.cpp +++ b/cpp2rust/converter/rules/rules_loader.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -252,26 +253,6 @@ class RuleUsageCollector llvm::DenseSet seen_; }; -llvm::json::Object ReadIndex(const std::filesystem::path &path) { - auto buf = llvm::MemoryBuffer::getFile(path.string()); - if (!buf) { - llvm::errs() << "Missing " << path.string() << ", run cpp-rule-indexer\n"; - llvm::report_fatal_error("cannot read the rule index"); - } - auto parsed = llvm::json::parse((*buf)->getBuffer()); - if (!parsed) { - llvm::errs() << "Failed to parse rule index: " << path.string() << ": " - << llvm::toString(parsed.takeError()) << '\n'; - llvm::report_fatal_error("cannot parse the rule index"); - } - auto index = parsed->getAsObject(); - if (!index) { - llvm::errs() << "Rule index is not an object: " << path.string() << '\n'; - llvm::report_fatal_error("cannot parse the rule index"); - } - return std::move(*index); -} - const llvm::json::Object &CIndex() { static const llvm::json::Object index = ReadIndex(std::filesystem::path(RULES_INDEX_DIR) / "c.json"); @@ -296,9 +277,30 @@ std::string WrapRule(const llvm::json::Object &rule, bool is_cxx) { dir, text); } +unsigned CxxStandard(const clang::LangOptions &opts) { + if (opts.CPlusPlus26) { + return 26; + } + if (opts.CPlusPlus23) { + return 23; + } + if (opts.CPlusPlus20) { + return 20; + } + if (opts.CPlusPlus17) { + return 17; + } + if (opts.CPlusPlus14) { + return 14; + } + return 11; +} + std::string BuildRulesEpilogue(const std::unordered_set &keys, - bool is_cxx) { + const clang::LangOptions &opts) { + bool is_cxx = opts.CPlusPlus; const auto &index = is_cxx ? CxxIndex() : CIndex(); + unsigned standard = is_cxx ? CxxStandard(opts) : 0; auto common = index.getString("common"); std::string out; if (!common->empty()) { @@ -312,6 +314,9 @@ std::string BuildRulesEpilogue(const std::unordered_set &keys, } for (const auto &entry : *entries) { auto rule = entry.getAsObject(); + if (auto since = rule->getInteger("since"); since && *since > standard) { + continue; + } if (auto required = rule->getArray("requires"); required && !llvm::all_of(*required, [&](const llvm::json::Value &key) { @@ -460,12 +465,35 @@ std::string TypeKey(clang::QualType type) { if (auto sugar = type->getAs()) { return sugar->getIdentifier()->getName().str(); } + if (auto builtin = type->getAs()) { + return builtin->getName(clang::PrintingPolicy(clang::LangOptions())).str(); + } if (auto tag = type->getAsTagDecl()) { return ClassKey(tag); } return {}; } +llvm::json::Object ReadIndex(const std::filesystem::path &path) { + auto buf = llvm::MemoryBuffer::getFile(path.string()); + if (!buf) { + llvm::errs() << "Missing rule index " << path.string() << '\n'; + llvm::report_fatal_error("cannot read the rule index"); + } + auto parsed = llvm::json::parse((*buf)->getBuffer()); + if (!parsed) { + llvm::errs() << "Failed to parse rule index: " << path.string() << ": " + << llvm::toString(parsed.takeError()) << '\n'; + llvm::report_fatal_error("cannot parse the rule index"); + } + auto index = parsed->getAsObject(); + if (!index) { + llvm::errs() << "Rule index is not an object: " << path.string() << '\n'; + llvm::report_fatal_error("cannot parse the rule index"); + } + return std::move(*index); +} + void PragmaHandler::HandlePragma(clang::Preprocessor &PP, clang::PragmaIntroducer introducer, clang::Token &tok) { @@ -482,7 +510,7 @@ void PragmaHandler::HandlePragma(clang::Preprocessor &PP, RuleUsageCollector collector(CI_.getSema(), keys); collector.TraverseDecl(ctx.getTranslationUnitDecl()); - auto text = BuildRulesEpilogue(keys, ctx.getLangOpts().CPlusPlus); + auto text = BuildRulesEpilogue(keys, ctx.getLangOpts()); log() << "rules loaded for this translation unit:\n" << text; auto rules_file = src_mgr.createFileID( llvm::MemoryBuffer::getMemBufferCopy(text, ""), diff --git a/cpp2rust/converter/rules/rules_loader.h b/cpp2rust/converter/rules/rules_loader.h index 6e99cd7f4..52919d519 100644 --- a/cpp2rust/converter/rules/rules_loader.h +++ b/cpp2rust/converter/rules/rules_loader.h @@ -10,7 +10,9 @@ #include #include #include +#include +#include #include namespace cpp2rust::RulesLoader { @@ -33,6 +35,8 @@ std::string ExprKey(clang::ASTContext &ctx, const clang::Expr *expr); std::string TypeKey(clang::QualType type); +llvm::json::Object ReadIndex(const std::filesystem::path &path); + class PragmaHandler : public clang::PragmaHandler { public: explicit PragmaHandler(clang::CompilerInstance &CI) diff --git a/cpp2rust/converter/rules/string_matcher.cpp b/cpp2rust/converter/rules/string_matcher.cpp deleted file mode 100644 index f6be074e9..000000000 --- a/cpp2rust/converter/rules/string_matcher.cpp +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright (c) 2022-present INESC-ID. -// Distributed under the MIT license that can be found in the LICENSE file. - -#include -#include -#include -#include - -#include "converter/converter_lib.h" -#include "converter/printer.h" -#include "converter/rules/matcher.h" -#include "converter/rules/registry.h" - -namespace cpp2rust::Matcher { - -namespace { - -// Attempts to unify an instantiated C++ type or function signature with a -// corresponding template pattern. If the two match structurally, it returns -// a mapping from template parameter names (e.g., "T1") to their concrete -// instantiated types (e.g., "int"). If no match is possible, returns nullopt. -// -// Example: -// template_str = "std::vector::vector()" -// instantiated = "std::vector::vector()" -// result = { "int" } -std::optional matchTemplate(const std::string &template_str, - const std::string &instantiated) { - auto matchLiteralAt = [&](const std::string &input_str, size_t pos, - std::string_view literal, size_t &end_pos) -> bool { - size_t i = pos; - size_t j = 0; - - while (true) { - while (i < input_str.size() && std::isspace(input_str[i])) { - i++; - } - - while (j < literal.size() && std::isspace(literal[j])) { - j++; - } - - if (j == literal.size()) { - end_pos = i; - return true; - } - - if (i >= input_str.size()) { - return false; - } - - if (input_str[i] != literal[j]) { - return false; - } - - i++; - j++; - } - }; - - auto findNextLiteralSameDepth = [&](const std::string &s, size_t start, - std::string_view lit) -> size_t { - int ang = 0; - int par = 0; - int sq = 0; - - for (size_t i = 0; i < s.size() && i < start; i++) { - switch (s[i]) { - case '<': { - ang++; - break; - } - case '>': { - ang--; - break; - } - case '(': { - par++; - break; - } - case ')': { - par--; - break; - } - case '[': { - sq++; - break; - } - case ']': { - sq--; - break; - } - default: - break; - } - assert(ang >= 0 && par >= 0 && sq >= 0 && "Unbalanced ang, par or sq"); - } - - int base_ang = ang; - int base_par = par; - int base_sq = sq; - - for (size_t i = start; i <= s.size(); i++) { - if (ang == base_ang && par == base_par && sq == base_sq) { - size_t end_i = 0; - if (matchLiteralAt(s, i, lit, end_i)) { - return i; - } - } - - if (i == s.size()) { - break; - } - - char c = s[i]; - switch (c) { - case '<': { - ang++; - break; - } - case '>': { - ang--; - break; - } - case '(': { - par++; - break; - } - case ')': { - par--; - break; - } - case '[': { - sq++; - break; - } - case ']': { - sq--; - break; - } - default: - break; - } - - if (ang < 0 || par < 0 || sq < 0) { - return std::string::npos; - } - } - - return std::string::npos; - }; - - Bindings captured; - - size_t ti = 0; - size_t si = 0; - - while (ti < template_str.size()) { - if (template_str[ti] == 'T' && ti + 1 < template_str.size() && - std::isdigit(template_str[ti + 1])) { - size_t tj = ti + 2; - while (tj < template_str.size() && std::isdigit(template_str[tj])) { - tj++; - } - - size_t type_idx = std::stoi(&template_str[ti + 1]) - 1; - assert(type_idx < TranslationRule::kMaxGenerics && - "template placeholder exceeds kMaxGenerics"); - ti = tj; - - std::string_view nextLit; - size_t scan = ti; - while (scan < template_str.size()) { - if (template_str[scan] == 'T' && scan + 1 < template_str.size() && - std::isdigit(template_str[scan + 1])) { - break; - } - scan++; - } - nextLit = std::string_view(template_str).substr(ti, scan - ti); - - captured.resize(std::max(captured.size(), type_idx + 1)); - auto &repl = captured[type_idx]; - if (repl.has_value()) { - size_t end_pos = 0; - if (!matchLiteralAt(instantiated, si, *repl, end_pos)) { - return std::nullopt; - } - si = end_pos; - } else { - if (!nextLit.empty()) { - size_t k = findNextLiteralSameDepth(instantiated, si, nextLit); - if (k == std::string::npos) { - return std::nullopt; - } - - size_t a = si; - size_t b = k; - - while (a < b && std::isspace(instantiated[a])) { - a++; - } - while (b > a && std::isspace(instantiated[b - 1])) { - b--; - } - - repl = instantiated.substr(a, b - a); - si = k; - } else { - size_t a = si; - size_t b = instantiated.size(); - - while (a < b && std::isspace(instantiated[a])) { - a++; - } - while (b > a && std::isspace(instantiated[b - 1])) { - b--; - } - - repl = instantiated.substr(a, b - a); - si = instantiated.size(); - } - } - - if (!nextLit.empty()) { - size_t end_pos = 0; - if (!matchLiteralAt(instantiated, si, nextLit, end_pos)) { - return std::nullopt; - } - si = end_pos; - ti += nextLit.size(); - } - } else { - size_t tj = ti; - while (tj < template_str.size()) { - if (template_str[tj] == 'T' && tj + 1 < template_str.size() && - std::isdigit(template_str[tj + 1])) { - break; - } - ++tj; - } - - auto lit = std::string_view(template_str).substr(ti, tj - ti); - size_t end_pos = 0; - if (!matchLiteralAt(instantiated, si, lit, end_pos)) { - return std::nullopt; - } - si = end_pos; - ti = tj; - } - } - - while (si < instantiated.size() && std::isspace(instantiated[si])) { - si++; - } - - if (si != instantiated.size()) { - return std::nullopt; - } - - return captured; -} - -std::string exprKey(const std::string &str) { - // Extract the function name from something like - // const T1 & std::foo::fn_name(args) - auto n = str.find_first_of('('); - if (n == std::string::npos) { - n = str.size(); - } - - // Walk backwards from '(' tracking <> depth: - // - skip characters inside template arguments (depth > 0) - // - stop at the first space outside all angle brackets - std::string result; - int depth = 0; - for (int i = (int)n - 1; i >= 0; --i) { - char c = str[i]; - if (c == '>') - ++depth; - else if (c == '<') - --depth; - else if (c == ' ' && depth == 0) - break; - else if (depth == 0) - result += c; - } - std::reverse(result.begin(), result.end()); - return result; -} - -std::string typeKey(const std::string &str) { - auto n = str.find_first_of("<["); - if (n == std::string::npos || str[n] == '<') { - return str.substr(0, n); - } - // something like int[][] or T1[] -> [] - return str.substr(n + 1); -} - -template -Match search(Candidates candidates, const std::string &txt) { - Rule *rule = nullptr; - Bindings subs; - - for (auto &[_, this_rule] : candidates) { - auto this_subs = matchTemplate(this_rule.src, txt); - if (!this_subs) { - continue; - } - // tie breaker: prefer more specific rules (usually the longer ones) - if (!rule || this_rule.src.size() > rule->src.size()) { - rule = &this_rule; - subs = *std::move(this_subs); - } - } - return {rule, std::move(subs)}; -} - -Match searchExpr(const std::string &txt) { - return search( - RuleRegistry::ExprCandidates(exprKey(txt)), txt); -} - -Match searchType(const std::string &txt) { - return search( - RuleRegistry::TypeCandidates(typeKey(txt)), txt); -} - -std::string mapTypeString(std::string_view cpp_type) { - auto [rule, subs] = searchType(std::string(cpp_type)); - if (!rule && cpp_type.starts_with("const ")) { - return mapTypeString(cpp_type.substr(std::string_view("const ").size())); - } - if (!rule) { - llvm::errs() << "cpp_type: " << cpp_type << '\n'; - assert(0 && "Type is not present in the registry"); - } - for (auto &ty : subs) { - if (ty) { - ty = mapTypeString(*ty); - } - } - return InstantiateTgt(subs, rule->type_info.type); -} - -} // namespace - -std::string Key(const TranslationRule::ExprRule &rule) { - return exprKey(rule.src); -} - -std::string Key(const TranslationRule::TypeRule &rule) { - return typeKey(rule.src); -} - -Match Find(clang::ASTContext &ctx, - const clang::Expr *expr) { - auto qualified_name = Printer::ToString(ctx, expr); - auto res = searchExpr(qualified_name); - log() << "search expr " << qualified_name << ", result:\n"; - if (res.first) { - res.first->dump(); - } else { - log() << "None\n"; - } - return res; -} - -Match Find(clang::ASTContext &ctx, - clang::QualType type) { - auto sugared = Printer::ToString(ctx, type, Printer::ScalarSugar::kPreserve); - if (auto res = searchType(sugared); res.first) { - log() << "search type " << sugared - << ", result: " << res.first->type_info.type << '\n'; - return res; - } - if (auto alias = type->getAs(); - alias && type.getCanonicalType()->isBuiltinType() && - clang::isa(alias->getDecl()->getDeclContext())) { - log() << "search type " << sugared << ", result: None\n"; - return Find(ctx, alias->getDecl()->getUnderlyingType()); - } - auto desugared = Printer::ToString(ctx, type); - if (desugared == sugared) { - log() << "search type " << desugared << ", result: None\n"; - return {}; - } - auto res = searchType(desugared); - log() << "search type " << desugared - << ", result: " << (res.first ? res.first->type_info.type : "None") - << '\n'; - return res; -} - -bool HasRuleNamed(clang::ASTContext &ctx, const clang::FunctionDecl *decl) { - return !RuleRegistry::ExprCandidates(exprKey(Printer::ToString(ctx, decl))) - .empty(); -} - -std::string MapBinding(const Bindings &bindings, unsigned n) { - return mapTypeString(bindings.at(n).value()); -} - -} // namespace cpp2rust::Matcher diff --git a/cpp2rust/converter/rules/type_matcher.cpp b/cpp2rust/converter/rules/type_matcher.cpp new file mode 100644 index 000000000..2e397fb61 --- /dev/null +++ b/cpp2rust/converter/rules/type_matcher.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/type_matcher.h" + +#include +#include +#include + +#include +#include + +#include "converter/rules/decls.h" + +namespace cpp2rust::AstMatcher { + +namespace { + +std::optional> +completeArgs(clang::Sema &sema, const clang::TypeAliasTemplateDecl *tmpl, + std::vector deduced) { + if (llvm::none_of(deduced, [](const clang::TemplateArgument &arg) { + return arg.isNull(); + })) { + return deduced; + } + auto loc = tmpl->getLocation(); + clang::TemplateArgumentListInfo explicit_args(loc, loc); + for (const auto &arg : deduced) { + if (arg.isNull()) { + break; + } + explicit_args.addArgument( + sema.getTrivialTemplateArgumentLoc(arg, clang::QualType(), loc)); + } + RuleDecls::Trial trial(sema); + clang::Sema::CheckTemplateArgumentInfo info; + if (sema.CheckTemplateArgumentList( + const_cast(tmpl), loc, explicit_args, + /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, info) || + trial.failed()) { + return std::nullopt; + } + return std::vector(info.CanonicalConverted.begin(), + info.CanonicalConverted.end()); +} + +std::vector getSugarChain(clang::ASTContext &ctx, + clang::QualType type) { + std::vector chain{type}; + for (auto next = type.getSingleStepDesugaredType(ctx); next != type; + next = type.getSingleStepDesugaredType(ctx)) { + type = next; + chain.push_back(type); + } + return chain; +} + +const clang::Type *getSugar(clang::QualType type) { + if (auto typeof_type = + llvm::dyn_cast(type.getTypePtr())) { + type = typeof_type->getUnderlyingExpr()->getType(); + } + if (llvm::isa( + type.getTypePtr())) { + return type.getTypePtr(); + } + return nullptr; +} + +bool isSameSugar(const clang::Type *a, const clang::Type *b) { + auto a_typedef = llvm::dyn_cast(a); + auto b_typedef = llvm::dyn_cast(b); + if (a_typedef || b_typedef) { + return a_typedef && b_typedef && + a_typedef->getDecl()->getCanonicalDecl() == + b_typedef->getDecl()->getCanonicalDecl(); + } + return a == b; +} + +} // namespace + +std::optional MatchType(clang::Sema &sema, clang::QualType use, + const clang::NamedDecl *rule) { + auto &ctx = sema.Context; + auto canonical = use.getCanonicalType().getUnqualifiedType(); + auto tmpl = llvm::dyn_cast(rule); + auto alias = tmpl ? tmpl->getTemplatedDecl() + : llvm::cast(rule); + auto pattern = alias->getUnderlyingType(); + auto chain = getSugarChain(ctx, use); + if (!tmpl) { + if (auto sugar = getSugar(pattern)) { + for (unsigned depth = 0; depth < chain.size(); ++depth) { + if (isSameSugar(chain[depth].getTypePtr(), sugar)) { + return TypeMatch{{}, depth}; + } + } + if (pattern->isBuiltinType()) { + return std::nullopt; + } + } + if (pattern.getCanonicalType().getUnqualifiedType() != canonical) { + return std::nullopt; + } + return TypeMatch{{}, kCanonicalDepth}; + } + auto params = tmpl->getTemplateParameters(); + if (auto member = pattern->getAs()) { + auto qualifier = member->getQualifier().getAsType(); + for (unsigned depth = 0; depth < chain.size() && qualifier; ++depth) { + auto typedef_type = + llvm::dyn_cast(chain[depth].getTypePtr()); + if (!typedef_type || + typedef_type->getDecl()->getIdentifier() != member->getIdentifier()) { + continue; + } + auto owner = llvm::dyn_cast( + typedef_type->getDecl()->getDeclContext()); + if (!owner) { + continue; + } + auto args = + RuleDecls::DeduceFromType(sema, params, clang::QualType(qualifier, 0), + ctx.getCanonicalTagType(owner)); + if (args && (args = completeArgs(sema, tmpl, *std::move(args)))) { + return TypeMatch{*std::move(args), depth}; + } + } + return std::nullopt; + } + auto args = RuleDecls::DeduceFromType(sema, params, pattern, canonical); + if (args && (args = completeArgs(sema, tmpl, *std::move(args)))) { + return TypeMatch{*std::move(args), kCanonicalDepth}; + } + return std::nullopt; +} + +const clang::NamedDecl *MoreSpecializedType(clang::Sema &sema, + const clang::NamedDecl *a, + const clang::NamedDecl *b) { + auto a_tmpl = llvm::dyn_cast(a); + auto b_tmpl = llvm::dyn_cast(b); + if (!a_tmpl || !b_tmpl) { + if (!a_tmpl && b_tmpl) { + return a; + } + return a_tmpl && !b_tmpl ? b : nullptr; + } + auto a_alias = llvm::dyn_cast(a); + auto b_alias = llvm::dyn_cast(b); + if (a_alias && b_alias) { + auto instance_of = [&](const clang::TypeAliasTemplateDecl *x, + const clang::TypeAliasTemplateDecl *y) { + return RuleDecls::DeduceFromType( + sema, y->getTemplateParameters(), + y->getTemplatedDecl()->getUnderlyingType(), + x->getTemplatedDecl()->getUnderlyingType().getCanonicalType()) + .has_value(); + }; + bool a_of_b = instance_of(a_alias, b_alias); + bool b_of_a = instance_of(b_alias, a_alias); + if (a_of_b != b_of_a) { + return a_of_b ? a : b; + } + } + return nullptr; +} + +} // namespace cpp2rust::AstMatcher diff --git a/cpp2rust/converter/rules/type_matcher.h b/cpp2rust/converter/rules/type_matcher.h new file mode 100644 index 000000000..fabc896c7 --- /dev/null +++ b/cpp2rust/converter/rules/type_matcher.h @@ -0,0 +1,28 @@ +#pragma once + +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include +#include +#include + +#include +#include + +namespace cpp2rust::AstMatcher { +inline constexpr unsigned kCanonicalDepth = -1u; + +struct TypeMatch { + std::vector args; + unsigned depth; +}; + +std::optional MatchType(clang::Sema &sema, clang::QualType use, + const clang::NamedDecl *rule); + +const clang::NamedDecl *MoreSpecializedType(clang::Sema &sema, + const clang::NamedDecl *a, + const clang::NamedDecl *b); +} // namespace cpp2rust::AstMatcher diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 34c7696c9..5beafea1f 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -4,7 +4,6 @@ #include "converter/translation_rule.h" #include -#include #include #include @@ -97,146 +96,6 @@ std::vector ParseBodyFragmentsJSON(const llvm::json::Array &arr) { return result; } -ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { - ExprRule ir; - - if (auto *params = obj.getObject("params")) { - for (auto &[key, val] : *params) { - if (auto *param_obj = val.getAsObject()) { - size_t id = atoi(llvm::StringRef(key).data() + 1); - ir.params.resize(std::max(ir.params.size(), id + 1)); - ir.params[id] = ParseTypeInfoJSON(*param_obj); - } - } - } - - if (auto *rt = obj.getObject("return_type")) { - ir.return_type = ParseTypeInfoJSON(*rt); - } - - if (auto ms = obj.getBoolean("multi_statement")) { - ir.multi_statement = *ms; - } - - if (auto v = obj.getBoolean("is_extern")) { - ir.is_extern = *v; - } - - if (auto *generics = obj.getObject("generics")) { - for (auto &[key, val] : *generics) { - if (auto *arr = val.getAsArray()) { - std::vector bounds; - for (auto &b : *arr) { - if (auto s = b.getAsString()) - bounds.push_back(s->str()); - } - size_t id = atoi(llvm::StringRef(key).data() + 1) - 1; // starts in T1 - ir.generics.resize(std::max(ir.generics.size(), id + 1)); - ir.generics[id] = std::move(bounds); - } - } - } - - if (auto *body = obj.getArray("body")) { - ir.body = ParseBodyFragmentsJSON(*body); - } - - return ir; -} - -TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { - TypeRule rule; - if (auto init = obj.getString("init")) - rule.initializer = init->str(); - rule.type_info = ParseTypeInfoJSON(obj); - return rule; -} - -void LoadTgtFromIR(ExprRules &exprs, TypeRules &types, - const std::filesystem::path &json_path) { - auto buf = llvm::MemoryBuffer::getFile(json_path.string()); - if (!buf) - return; - - auto parsed = llvm::json::parse((*buf)->getBuffer()); - if (!parsed) { - llvm::errs() << "Failed to parse IR JSON: " << json_path << ": " - << llvm::toString(parsed.takeError()) << '\n'; - assert(0); - return; - } - - auto *root = parsed->getAsObject(); - if (!root) - return; - - for (auto &[entry_name, entry_val] : *root) { - auto *obj = entry_val.getAsObject(); - if (!obj) - continue; - - auto name = entry_name.str(); - if (name[0] == 'f') { - exprs[std::move(name)] = ParseExprRuleJSON(*obj); - } else if (name[0] == 't') { - types[std::move(name)] = ParseTypeRuleJSON(*obj); - } - } -} - -void LoadIrSrc(ExprRules &exprs, TypeRules &types, - const std::filesystem::path &json_path) { - auto buf = llvm::MemoryBuffer::getFile(json_path.string()); - if (!buf) { - llvm::errs() << "Missing " << json_path << ", run cpp-rule-preprocessor\n"; - assert(0); - return; - } - - auto parsed = llvm::json::parse((*buf)->getBuffer()); - if (!parsed) { - llvm::errs() << "Failed to parse IR src JSON: " << json_path << ": " - << llvm::toString(parsed.takeError()) << '\n'; - assert(0); - return; - } - - auto *root = parsed->getAsObject(); - if (!root) { - return; - } - - for (auto &[entry_name, entry_val] : *root) { - auto name = entry_name.str(); - auto val = entry_val.getAsString(); - if (name[0] == 'f') { - auto it = exprs.find(name); - if (it == exprs.end()) { - llvm::errs() << name << '\n'; - assert(0 && "ir_src.json expr entry has no matching IR target rule"); - } - if (auto *obj = entry_val.getAsObject()) { - it->second.src = obj->getString("key")->str(); - auto *init_type = obj->getObject("init_type"); - assert(init_type && "ir_src.json expr entry object without init_type"); - it->second.init_type = InitTypeLocation{ - (unsigned)*init_type->getInteger("depth"), - (unsigned)*init_type->getInteger("index"), - }; - continue; - } - it->second.src = val->str(); - } else if (name[0] == 't') { - auto it = types.find(name); - if (it == types.end()) { - llvm::errs() << name << '\n'; - assert(0 && "ir_src.json type entry has no matching IR target rule"); - } - it->second.src = val->str(); - } - } -} - void BodyFragmentDump(const BodyFragment &frag) { if (auto *t = std::get_if(&frag)) { t->dump(); @@ -311,11 +170,6 @@ void MethodCallFragment::dump() const { } void ExprRule::dump() const { - log() << "Matching: " << src << '\n'; - if (init_type.valid()) { - log() << " init type: depth " << init_type.depth << ", index " - << init_type.index << '\n'; - } unsigned i = 0; for (auto &info : params) { log() << " param a" << i++ << ": "; @@ -340,46 +194,7 @@ void ExprRule::dump() const { } } -void ExprRule::validate(const std::string &name) const { - if (src.empty()) { - llvm::errs() << name << '\n'; - dump(); - llvm::report_fatal_error("Expr rule loaded from IR but has no src"); - } - - if (HasInitFragment(body) && !init_type.valid()) { - llvm::errs() << name << '\n'; - dump(); - llvm::report_fatal_error( - "Expr rule uses init but its src pack is not declared as Init"); - } - - if (generics.empty()) - return; - - bool has_generic[kMaxGenerics] = {false}; - for (size_t i = 0, e = src.size(); i < e; ++i) { - auto pos = src.find('T', i); - if (pos == std::string::npos) - break; - auto ch = pos + 1 < e ? src[pos + 1] : '\0'; - if (ch >= '1' && ch <= '9') { - has_generic[ch - '1'] = true; - i = pos + 1; - } - } - - for (size_t i = 0, e = generics.size(); i < e; ++i) { - if (!has_generic[i]) { - llvm::errs() << name << '\n'; - dump(); - llvm::errs() << "generic T" << (i + 1) - << " declared but missing from src: " << src << '\n'; - llvm::report_fatal_error("Absent generic from src"); - } - } -} +bool ExprRule::usesInit() const { return HasInitFragment(body); } void GenericFragment::dump() const { log() << " generic: " << n << '\n'; } @@ -394,7 +209,7 @@ void TypeInfo::dump() const { } void TypeRule::dump() const { - log() << "name: " << src << "\n Rust type: "; + log() << "Rust type: "; type_info.dump(); log() << '\n'; if (!initializer.empty()) { @@ -402,32 +217,59 @@ void TypeRule::dump() const { } } -std::pair Load(const std::filesystem::path &dir, - Model model) { - ExprRules exprs; - TypeRules types; - LoadTgtFromIR(exprs, types, dir / "ir_unsafe.json"); +ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { + ExprRule ir; - if (model == Model::kRefCount) { - auto refcount_ir_path = dir / "ir_refcount.json"; - if (std::filesystem::exists(refcount_ir_path)) { - LoadTgtFromIR(exprs, types, refcount_ir_path); + if (auto *params = obj.getObject("params")) { + for (auto &[key, val] : *params) { + if (auto *param_obj = val.getAsObject()) { + size_t id = atoi(llvm::StringRef(key).data() + 1); + ir.params.resize(std::max(ir.params.size(), id + 1)); + ir.params[id] = ParseTypeInfoJSON(*param_obj); + } } } - LoadIrSrc(exprs, types, dir / "ir_src.json"); + if (auto *rt = obj.getObject("return_type")) { + ir.return_type = ParseTypeInfoJSON(*rt); + } + + if (auto ms = obj.getBoolean("multi_statement")) { + ir.multi_statement = *ms; + } - for (auto &[name, rule] : exprs) { - rule.validate(name); + if (auto v = obj.getBoolean("is_extern")) { + ir.is_extern = *v; } - for (auto &[name, rule] : types) { - if (rule.src.empty()) { - llvm::errs() << name << '\n'; - rule.dump(); - assert(0 && "Type rule loaded from IR but has no src"); + + if (auto *generics = obj.getObject("generics")) { + for (auto &[key, val] : *generics) { + if (auto *arr = val.getAsArray()) { + std::vector bounds; + for (auto &b : *arr) { + if (auto s = b.getAsString()) + bounds.push_back(s->str()); + } + size_t id = atoi(llvm::StringRef(key).data() + 1) - 1; // starts in T1 + ir.generics.resize(std::max(ir.generics.size(), id + 1)); + ir.generics[id] = std::move(bounds); + } } } - return {std::move(exprs), std::move(types)}; + + if (auto *body = obj.getArray("body")) { + ir.body = ParseBodyFragmentsJSON(*body); + } + + return ir; +} + +TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj) { + TypeRule rule; + if (auto init = obj.getString("init")) + rule.initializer = init->str(); + rule.type_info = ParseTypeInfoJSON(obj); + return rule; } } // namespace cpp2rust::TranslationRule diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 8d6846ef8..6cc21da79 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -4,6 +4,7 @@ // Distributed under the MIT license that can be found in the LICENSE file. #include +#include #include #include @@ -72,16 +73,7 @@ struct TypeInfo { void dump() const; }; -struct InitTypeLocation { - unsigned depth = -1u; - unsigned index = -1u; - - bool valid() const { return depth != -1u; } -}; - struct ExprRule { - std::string src; - InitTypeLocation init_type; std::vector params; TypeInfo return_type; std::vector> generics; // "T1" -> ["Ord", "Clone"] @@ -90,30 +82,17 @@ struct ExprRule { bool is_extern = false; void dump() const; - void validate(const std::string &name) const; + bool usesInit() const; }; struct TypeRule { - std::string src; std::string initializer; // Rust initializer expression TypeInfo type_info; void dump() const; - - static TypeRule Plain(std::string type) { - return {{}, {}, {{}, std::move(type), false, false}}; - } - static TypeRule RefcountPtr(std::string type) { - return {{}, {}, {{}, std::move(type), true, false}}; - } - static TypeRule UnsafePtr(std::string type) { - return {{}, {}, {{}, std::move(type), false, true}}; - } }; -using ExprRules = std::unordered_map; -using TypeRules = std::unordered_map; +ExprRule ParseExprRuleJSON(const llvm::json::Object &obj); -std::pair Load(const std::filesystem::path &dir, - Model model); +TypeRule ParseTypeRuleJSON(const llvm::json::Object &obj); } // namespace cpp2rust::TranslationRule diff --git a/cpp2rust/cpp2rust.cpp b/cpp2rust/cpp2rust.cpp index d8ffcb747..a1ee64393 100644 --- a/cpp2rust/cpp2rust.cpp +++ b/cpp2rust/cpp2rust.cpp @@ -94,8 +94,8 @@ static bool HasIRFiles(const fs::path &dir) { continue; } const auto &p = it->path(); - if (fs::exists(p / "ir_src.json") && (fs::exists(p / "ir_unsafe.json") || - fs::exists(p / "ir_refcount.json"))) { + if (fs::exists(p / "ir_unsafe.json") || + fs::exists(p / "ir_refcount.json")) { return true; } } @@ -164,10 +164,8 @@ int main(int argc, char *argv[]) { std::vector cxx_flags(CXXFlags.begin(), CXXFlags.end()); - if (RulesDir.empty() && !ResolveRulesDir()) { - llvm::errs() << "ERROR: rules directory not found. " - "Please specify one with --rules\n"; - return EXIT_FAILURE; + if (RulesDir.empty()) { + ResolveRulesDir(); } auto rs_code = diff --git a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp index 9aa7dfc22..2478d6f2c 100644 --- a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp +++ b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp @@ -21,12 +21,12 @@ #include #include #include -#include #include #include #include #include "compat/platform_flags.h" +#include "converter/rules/decls.h" #include "converter/rules/rules_loader.h" namespace fs = std::filesystem; @@ -134,6 +134,11 @@ std::string ClassOfParameters(const clang::Decl *rule) { return {}; } +std::string ClassOfEnableIf(const clang::Decl *rule) { + auto same = RuleDecls::EnableIfSameType(rule); + return same.isNull() ? std::string() : ClassOf(same); +} + const clang::Expr *RuleExpr(const clang::Decl *decl) { if (auto var = llvm::dyn_cast(decl)) { return var->getInit(); @@ -171,6 +176,14 @@ std::string DependentExprKey(const clang::Expr *expr) { return RulesLoader::DeclKey( (*overloads->decls_begin())->getUnderlyingDecl()); } + if (auto ref = llvm::dyn_cast(callee)) { + auto qualifier = ref->getQualifier(); + if (qualifier.getKind() == clang::NestedNameSpecifier::Kind::Type) { + return RulesLoader::MemberKey( + ClassOf(clang::QualType(qualifier.getAsType(), 0)), + RulesLoader::MemberName(ref->getDeclName())); + } + } if (auto lookup = llvm::dyn_cast(callee); lookup && lookup->getQualifier() && !llvm::isa(call) && @@ -228,7 +241,6 @@ void AddRule(llvm::json::Object &rules, std::string key, void IndexRuleFile(clang::ASTContext &ctx, const std::string &dir_name, llvm::json::Object &rules) { - bool is_c = !ctx.getLangOpts().CPlusPlus; auto &sm = ctx.getSourceManager(); for (auto *decl : ctx.getTranslationUnitDecl()->decls()) { if (decl->isImplicit() || @@ -254,6 +266,9 @@ void IndexRuleFile(clang::ASTContext &ctx, const std::string &dir_name, if (key.empty()) { key = ClassOfParameters(decl); } + if (key.empty()) { + key = ClassOfEnableIf(decl); + } if (key.empty()) { llvm::errs() << "ERROR: cannot derive the key of rule '" << name << "' in rule dir " << dir_name << '\n'; @@ -261,10 +276,7 @@ void IndexRuleFile(clang::ASTContext &ctx, const std::string &dir_name, } auto range = clang::tooling::getExtendedRange(*decl, clang::tok::semi, ctx); auto text = clang::tooling::getText(range, ctx).str() + '\n'; - llvm::json::Object rule{{"dir", dir_name}, {"text", text}}; - if (is_c) { - rule["name"] = name; - } + llvm::json::Object rule{{"dir", dir_name}, {"name", name}, {"text", text}}; if (auto required = RequiredClasses(decl, alias); !required.empty()) { rule["requires"] = llvm::json::Array(required); } @@ -309,54 +321,91 @@ void WriteJson(const fs::path &path, llvm::json::Object object) { os << llvm::json::Value(std::move(object)); } -std::vector ReadCXXFlags(const fs::path &rule_dir) { - std::vector flags; - std::ifstream file(rule_dir / "cxxflags"); - for (std::string flag; std::getline(file, flag);) { - if (!flag.empty()) { - flags.push_back(flag); - } +llvm::json::Object ReadJson(const fs::path &path) { + auto buffer = llvm::MemoryBuffer::getFile(path.string()); + if (!buffer) { + llvm::errs() << "ERROR: cannot read " << path.string() << '\n'; + std::exit(EXIT_FAILURE); + } + auto parsed = llvm::json::parse((*buffer)->getBuffer()); + if (!parsed || !parsed->getAsObject()) { + llvm::errs() << "ERROR: cannot parse " << path.string() << '\n'; + std::exit(EXIT_FAILURE); } - return flags; + return std::move(*parsed->getAsObject()); } -void IndexRules(const fs::path &rules_dir, const fs::path &index_dir, - const std::vector &excluded, - const std::vector &common_headers) { - std::vector rule_dirs; - for (const auto &entry : fs::directory_iterator(rules_dir)) { - if (entry.is_directory() && - !llvm::is_contained(excluded, entry.path().filename().string())) { - rule_dirs.push_back(entry.path()); +unsigned SinceStandard(const fs::path &src_path) { + auto stem_string = src_path.stem().string(); + llvm::StringRef stem = stem_string; + unsigned standard = 11; + if (stem.consume_front("src_cxx") && stem.getAsInteger(10, standard)) { + llvm::errs() << "ERROR: unexpected rule file " << src_path.string() << '\n'; + std::exit(EXIT_FAILURE); + } + return standard; +} + +void MergeRules(llvm::json::Object &into, llvm::json::Object &&from) { + for (auto &[key, entries] : from) { + for (auto &entry : *entries.getAsArray()) { + AddRule(into, key.str(), std::move(entry)); } } - std::ranges::sort(rule_dirs); - fs::create_directories(index_dir); - for (const char *lang : {"c", "cpp"}) { - std::string common; - if (lang == std::string("cpp")) { - for (const auto &header : common_headers) { - common += std::format("#include \"{}\"\n", header); - } +} + +void IndexDir(const fs::path &rule_dir, const fs::path &out) { + auto dir_name = rule_dir.filename().string(); + llvm::json::Object c_rules; + if (fs::exists(rule_dir / "src.c")) { + Index(rule_dir / "src.c", /*is_c=*/true, dir_name, {}, c_rules); + } + std::vector cxx_srcs; + for (const auto &entry : fs::directory_iterator(rule_dir)) { + auto name = entry.path().filename().string(); + if (name == "src.c" || name == "src.cpp" || + (name.starts_with("src_cxx") && name.ends_with(".cpp"))) { + cxx_srcs.push_back(entry.path()); } - bool is_c = lang == std::string("c"); + } + std::ranges::sort(cxx_srcs); + llvm::json::Object cxx_rules; + for (const auto &src_path : cxx_srcs) { + auto standard = SinceStandard(src_path); llvm::json::Object rules; - for (const auto &rule_dir : rule_dirs) { - for (const char *name : {"src.c", "src.cpp"}) { - auto src_path = rule_dir / name; - if (!fs::exists(src_path) || (is_c && src_path.extension() != ".c")) { - continue; - } - llvm::errs() << "Indexing " << src_path.string() << " as " << lang - << '\n'; - Index(src_path, is_c, rule_dir.filename().string(), - ReadCXXFlags(rule_dir), rules); + Index(src_path, /*is_c=*/false, dir_name, + {std::format("-std=c++{}", standard)}, rules); + for (auto &[key, entries] : rules) { + for (auto &entry : *entries.getAsArray()) { + (*entry.getAsObject())["since"] = standard; } } - WriteJson(index_dir / (std::string(lang) + ".json"), - llvm::json::Object{{"common", std::move(common)}, - {"rules", std::move(rules)}}); + MergeRules(cxx_rules, std::move(rules)); } + WriteJson(out, llvm::json::Object{{"c", std::move(c_rules)}, + {"cpp", std::move(cxx_rules)}}); +} + +void MergeIndexes(const std::vector &parts, + const std::vector &common_headers, + const fs::path &index_dir) { + llvm::json::Object c_rules; + llvm::json::Object cxx_rules; + for (const auto &part : parts) { + auto json = ReadJson(part); + MergeRules(c_rules, std::move(*json.getObject("c"))); + MergeRules(cxx_rules, std::move(*json.getObject("cpp"))); + } + std::string common; + for (const auto &header : common_headers) { + common += std::format("#include \"{}\"\n", header); + } + fs::create_directories(index_dir); + WriteJson(index_dir / "c.json", + llvm::json::Object{{"common", ""}, {"rules", std::move(c_rules)}}); + WriteJson(index_dir / "cpp.json", + llvm::json::Object{{"common", std::move(common)}, + {"rules", std::move(cxx_rules)}}); } } // namespace cpp2rust @@ -365,23 +414,35 @@ namespace { llvm::cl::OptionCategory cat("cpp-rule-indexer options"); -llvm::cl::list - Excluded("exclude", llvm::cl::desc("Rule directory that is not indexed"), - llvm::cl::value_desc("rule-dir"), llvm::cl::ZeroOrMore, - llvm::cl::cat(cat)); +llvm::cl::opt Dir("dir", llvm::cl::desc("Rule directory to index"), + llvm::cl::value_desc("rule-dir"), + llvm::cl::cat(cat)); + +llvm::cl::list Parts(llvm::cl::Positional, + llvm::cl::desc(""), + llvm::cl::ZeroOrMore, llvm::cl::cat(cat)); llvm::cl::list CommonHeaders( "common-header", llvm::cl::desc("Header the C++ rules depend on"), llvm::cl::value_desc("header"), llvm::cl::ZeroOrMore, llvm::cl::cat(cat)); +llvm::cl::opt + Out("o", + llvm::cl::desc("Output part of a rule directory, or index directory"), + llvm::cl::value_desc("path"), llvm::cl::Required, llvm::cl::cat(cat)); + } // namespace int main(int argc, char *argv[]) { llvm::cl::HideUnrelatedOptions(cat); llvm::cl::ParseCommandLineOptions(argc, argv); - cpp2rust::IndexRules(RULES_SRC_DIR, RULES_INDEX_DIR, - {Excluded.begin(), Excluded.end()}, - {CommonHeaders.begin(), CommonHeaders.end()}); + if (!Dir.empty()) { + cpp2rust::IndexDir(Dir.getValue(), Out.getValue()); + } else { + cpp2rust::MergeIndexes({Parts.begin(), Parts.end()}, + {CommonHeaders.begin(), CommonHeaders.end()}, + Out.getValue()); + } return EXIT_SUCCESS; } diff --git a/cpp2rust/rule_preprocessing/cpp_rule_preprocessor.cpp b/cpp2rust/rule_preprocessing/cpp_rule_preprocessor.cpp deleted file mode 100644 index d140b5ad7..000000000 --- a/cpp2rust/rule_preprocessing/cpp_rule_preprocessor.cpp +++ /dev/null @@ -1,1151 +0,0 @@ -// Copyright (c) 2022-present INESC-ID. -// Distributed under the MIT license that can be found in the LICENSE file. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "compat/platform_flags.h" -#include "converter/converter_lib.h" -#include "converter/printer.h" - -namespace fs = std::filesystem; - -namespace cpp2rust { - -enum LookupKind { RegularName, CXXMethodName, CXXConstructorName, ADL }; - -struct LookupInfo { - clang::DeclarationName name; - LookupKind kind; - llvm::ArrayRef explicitArgs; - - LookupInfo(const clang::Expr *expr) { - if (const auto *ul = llvm::dyn_cast(expr)) { - clang::DeclarationName dname = ul->getName(); - name = dname; - if (ul->requiresADL()) { - kind = LookupKind::ADL; - } else { - kind = LookupKind::RegularName; - } - explicitArgs = ul->template_arguments(); - } else if (const auto *dm = - llvm::dyn_cast(expr)) { - name = dm->getMember(); - kind = LookupKind::CXXMethodName; - explicitArgs = dm->template_arguments(); - } else if (const auto *um = - llvm::dyn_cast(expr)) { - name = um->getMemberName(); - kind = LookupKind::CXXMethodName; - explicitArgs = um->template_arguments(); - } else if (const auto *dref = - llvm::dyn_cast(expr)) { - clang::DeclarationName dname = dref->getDeclName(); - if (dname.getNameKind() == - clang::DeclarationName::NameKind::CXXConstructorName) { - name = dname; - kind = LookupKind::CXXConstructorName; - } else { - assert(0 && "Unsupported dref name kind"); - } - } else if (llvm::isa(expr)) { - kind = LookupKind::CXXConstructorName; - } else { - expr->dump(); - assert(0 && "Unsupported lookup expression"); - } - } -}; - -class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { -public: - explicit Callback(llvm::json::Object &out) : out_(out) {} - - void init(clang::Sema &sema) { - sema_ = &sema; - clang::SourceManager &sm = sema.Context.getSourceManager(); - loc_ = sm.getLocForStartOfFile(sm.getMainFileID()); - } - - void run(const clang::ast_matchers::MatchFinder::MatchResult &R) override { - assert(sema_); - if (auto func = R.Nodes.getNodeAs("validate_func")) { - const char *err = nullptr; - if (auto body = - clang::dyn_cast_or_null(func->getBody())) { - if (body->size() != 1) { - err = "body must contain exactly one statement (a return)"; - } else if (!clang::isa(*body->body_begin())) { - err = "body must be a return statement"; - } - } else { - err = "body cannot be empty"; - } - - if (err) { - llvm::errs() << "ERROR: " << func->getQualifiedNameAsString() << ": " - << err << '\n'; - std::exit(EXIT_FAILURE); - } - return; - } - if (auto var = R.Nodes.getNodeAs("fvar")) { - out_.try_emplace( - var->getQualifiedNameAsString(), - Printer::ToString(*R.Context, - R.Nodes.getNodeAs("fn"))); - return; - } - - if (auto var = R.Nodes.getNodeAs("tvar")) { - clang::QualType type = var->getUnderlyingType(); - if (auto *alias = llvm::dyn_cast(var)) { - if (auto *tdecl = alias->getDescribedAliasTemplate()) { - type = lookupType(tdecl); - } - } - auto src = - Printer::ToString(*R.Context, type, Printer::ScalarSugar::kPreserve); - out_.try_emplace(var->getQualifiedNameAsString(), std::move(src)); - return; - } - - if (auto func = R.Nodes.getNodeAs("func")) { - auto add = [&](std::string &&src) { - out_.try_emplace(func->getQualifiedNameAsString(), std::move(src)); - }; - - if (const auto *fcall = R.Nodes.getNodeAs("fcall")) { - if (fcall->getDirectCallee()) { - add(Printer::ToString(*R.Context, fcall)); - return; - } - - LookupInfo lookup(fcall->getCallee()); - clang::FunctionDecl *rule = nullptr; - clang::FunctionDecl *decl = lookupCalledDecl( - func->getDescribedFunctionTemplate(), lookup, &rule); - if (HasFunctionParameterPack(func) && HasFunctionParameterPack(decl)) { - addPackRule(func, rule, decl); - return; - } - add(Printer::ToString(*R.Context, decl)); - return; - } - if (const auto *ctor = - R.Nodes.getNodeAs("ctor")) { - if (ctor->getConstructor()) { - add(Printer::ToString(*R.Context, ctor)); - return; - } - } - if (const auto *muse = R.Nodes.getNodeAs("muse")) { - if (llvm::isa(muse->getMemberDecl())) { - add(Printer::ToString(*R.Context, muse)); - return; - } - } - if (const auto *um = - R.Nodes.getNodeAs("umuse")) { - add(Printer::ToString(*R.Context, um)); - return; - } - if (R.Nodes.getNodeAs("declref")) { - if (const auto *enum_val = - R.Nodes.getNodeAs("enum_val")) { - add(Printer::ToString(*R.Context, enum_val)); - return; - } else if (const auto *decl = - R.Nodes.getNodeAs("decl")) { - add(Printer::ToString(*R.Context, decl)); - return; - } - } - if (const auto *uop = - R.Nodes.getNodeAs("udeclref")) { - add(Printer::ToString(*R.Context, uop)); - return; - } - if (const auto *dsme = - R.Nodes.getNodeAs("dsme")) { - if (dsme->isArrow()) { - clang::MemberExpr *expr = lookupArrowAccess( - func->getDescribedFunctionTemplate(), dsme->getMemberNameInfo(), - dsme->getQualifierLoc()); - add(Printer::ToString(*R.Context, expr)); - return; - } - clang::NamedDecl *decl = lookupMemberAccess( - func->getDescribedFunctionTemplate(), dsme->getMember()); - add(Printer::ToString(*R.Context, decl)); - return; - } - if (const auto *uctor = - R.Nodes.getNodeAs("uctor")) { - LookupInfo lookup(uctor); - clang::NamedDecl *decl = lookupCalledDecl( - func->getDescribedFunctionTemplate(), lookup, nullptr); - add(Printer::ToString(*R.Context, decl)); - return; - } - if (const auto *lit = - R.Nodes.getNodeAs("macro_int")) { - if (lit->getBeginLoc().isMacroID()) { - add(Printer::ToString(*R.Context, lit)); - } - return; - } - } - } - -private: - llvm::json::Object &out_; - clang::Sema *sema_ = nullptr; - clang::SourceLocation loc_; - - void addPackRule(const clang::FunctionDecl *func, clang::FunctionDecl *rule, - clang::FunctionDecl *callee) { - auto key = Printer::ToString(sema_->Context, callee); - auto init_type = getInitType(func, rule); - if (init_type.isNull()) { - out_.try_emplace(func->getQualifiedNameAsString(), std::move(key)); - return; - } - - auto [depth, index] = findTemplateArgument(callee, init_type); - out_.try_emplace(func->getQualifiedNameAsString(), - llvm::json::Object{ - {"key", std::move(key)}, - {"init_type", llvm::json::Object{{"depth", depth}, - {"index", index}}}, - }); - } - - clang::QualType getInitType(const clang::FunctionDecl *func, - clang::FunctionDecl *rule) { - auto pattern = func->parameters() - .back() - ->getType() - ->castAs() - ->getPattern() - .getNonReferenceType(); - const auto *alias = pattern->getAs(); - if (!alias || !alias->isTypeAlias() || - alias->getTemplateName().getAsTemplateDecl()->getName() != "Init") { - return clang::QualType(); - } - - auto *tmpl = func->getDescribedFunctionTemplate(); - const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, tmpl); - return getSubstType(Inst, alias->template_arguments()[0].getAsType(), - rule->getTemplateSpecializationArgs()->asArray()); - } - - std::pair - findTemplateArgument(const clang::FunctionDecl *callee, - clang::QualType type) { - auto args = sema_->getTemplateInstantiationArgs(callee); - for (unsigned depth = 0; depth < args.getNumLevels(); ++depth) { - for (unsigned index = 0; index < args.getNumSubsitutedArgs(depth); - ++index) { - const auto &arg = args(depth, index); - if (arg.getKind() == clang::TemplateArgument::Type && - sema_->Context.hasSameType(arg.getAsType(), type)) { - return {depth, index}; - } - } - } - llvm::errs() << "ERROR: Init type " - << Printer::ToString(sema_->Context, type) - << " is not a template argument of " - << Printer::ToString(sema_->Context, callee) << '\n'; - std::exit(EXIT_FAILURE); - } - - void forceCompleteDefinition(clang::QualType type) { - type = type.getCanonicalType(); - if (type->isPointerType()) { - type = type->getPointeeType(); - } - - if (!type->isIncompleteType()) { - return; - } - - sema_->RequireCompleteType(loc_, type, - clang::Sema::CompleteTypeKind::Normal, - clang::diag::err_incomplete_type); - - if (auto *spec = - llvm::dyn_cast_or_null( - type->getAsCXXRecordDecl())) { - for (const auto *decl : spec->decls()) { - if (const auto *tdef = llvm::dyn_cast(decl)) { - clang::QualType tdef_t = tdef->getUnderlyingType(); - forceCompleteDefinition(tdef_t); - } - } - - for (const auto &arg : spec->getTemplateArgs().asArray()) { - if (arg.getKind() == clang::TemplateArgument::Type) { - forceCompleteDefinition(arg.getAsType()); - } - } - } - } - - clang::FunctionDecl *deduceTemplateArguments( - clang::FunctionTemplateDecl *decl, llvm::ArrayRef callArgs, - clang::QualType obj_t, clang::Expr::Classification exprClass, - clang::TemplateArgumentListInfo *explicitArgs = nullptr) { - clang::FunctionDecl *spec = nullptr; - clang::sema::TemplateDeductionInfo info((loc_)); - auto check = [](llvm::ArrayRef, bool) -> bool { - return false; - }; - - auto result = sema_->DeduceTemplateArguments( - decl, explicitArgs, callArgs, spec, info, false, false, false, obj_t, - exprClass, false, check); - - if (result == clang::TemplateDeductionResult::Success) { - return spec; - } - - if (result == clang::TemplateDeductionResult::SubstitutionFailure || - result == clang::TemplateDeductionResult::ConstraintsNotSatisfied) { - if (const auto *deduced = info.takeCanonical()) { - clang::TemplateArgumentListInfo targsInfo; - for (const auto &arg : deduced->asArray()) { - targsInfo.addArgument( - sema_->getTrivialTemplateArgumentLoc(arg, {}, loc_)); - } - - clang::DefaultArguments defaultArgs; - clang::Sema::CheckTemplateArgumentInfo ctai; - clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); - auto invalid = sema_->CheckTemplateArgumentList( - decl, decl->getTemplateParameters(), loc_, targsInfo, defaultArgs, - true, ctai); - - if (!invalid) { - return sema_->InstantiateFunctionDeclaration(decl, deduced, loc_); - } - } - } - - return nullptr; - } - - clang::NamespaceDecl *createNamespaceDecl() { - auto &ctx = sema_->getASTContext(); - auto *tu = ctx.getTranslationUnitDecl(); - auto *ns = clang::NamespaceDecl::Create(ctx, tu, false, loc_, loc_, nullptr, - nullptr, false); - tu->addDecl(ns); - return ns; - } - - clang::RecordDecl * - createRecordDecl(llvm::StringRef name, - clang::QualType base = clang::QualType()) { - bool owned = true; - bool dependent = false; - clang::CXXScopeSpec scope; - clang::MultiTemplateParamsArg args; - auto decl = sema_->ActOnTag( - sema_->getCurScope(), clang::DeclSpec::TST_struct, - clang::TagUseKind::Definition, loc_, scope, - &sema_->Context.Idents.get(name), loc_, clang::ParsedAttributesView(), - clang::AS_none, loc_, args, owned, dependent, loc_, false, - clang::TypeResult(), false, false, clang::OffsetOfKind::Outside); - assert(decl.isUsable() && "Record decl creation failed"); - auto *rdecl = decl.getAs(); - - rdecl->startDefinition(); - if (!base.isNull()) { - clang::CXXBaseSpecifier baseSpec( - clang::SourceRange(loc_, loc_), false, true, clang::AS_public, - sema_->Context.getTrivialTypeSourceInfo(base, loc_), - /*EllipsisLoc=*/clang::SourceLocation()); - const clang::CXXBaseSpecifier *bases[] = {&baseSpec}; - llvm::cast(rdecl)->setBases(bases, 1); - } - rdecl->completeDefinition(); - return rdecl; - } - - static clang::QualType getNTTPType(const clang::TemplateArgument &arg) { - switch (arg.getKind()) { - case clang::TemplateArgument::Integral: - return arg.getIntegralType(); - case clang::TemplateArgument::Declaration: - return arg.getParamTypeForDecl(); - case clang::TemplateArgument::NullPtr: - return arg.getNullPtrType(); - case clang::TemplateArgument::StructuralValue: - return arg.getStructuralValueType(); - default: - return clang::QualType(); - } - } - - clang::QualType - getTemplateIdType(clang::ClassTemplateDecl *decl, - llvm::ArrayRef args) { - clang::TemplateArgumentListInfo info(loc_, loc_); - for (const clang::TemplateArgument &arg : args) { - info.addArgument( - sema_->getTrivialTemplateArgumentLoc(arg, getNTTPType(arg), loc_)); - } - return sema_->CheckTemplateIdType(clang::ElaboratedTypeKeyword::None, - clang::TemplateName(decl), loc_, info, - sema_->getCurScope(), - /*ForNestedNameSpecifier=*/false); - } - - using MirrorMap = llvm::SmallDenseMap; - - clang::TypeSourceInfo *findMatch(MirrorMap &substs, clang::QualType type) { - const auto *tst = type->getAs(); - if (!tst) { - return nullptr; - } - - const auto *tdecl = llvm::dyn_cast_or_null( - tst->getTemplateName().getAsTemplateDecl()); - if (!tdecl) { - return nullptr; - } - - if (auto it = substs.find(tdecl->getCanonicalDecl()); it != substs.end()) { - auto match = getTemplateIdType(it->second, tst->template_arguments()); - assert(!match.isNull()); - return sema_->Context.getTrivialTypeSourceInfo(match, loc_); - } - return nullptr; - } - - clang::ClassTemplateDecl * - createInheritingTemplate(llvm::StringRef name, clang::ClassTemplateDecl *decl, - MirrorMap &substs) { - clang::ASTContext &ctx = sema_->Context; - auto *pattern = clang::CXXRecordDecl::Create( - ctx, clang::TagTypeKind::Struct, sema_->CurContext, loc_, loc_, - &ctx.Idents.get(name)); - - auto *mirror = clang::ClassTemplateDecl::Create( - ctx, sema_->CurContext, loc_, - clang::DeclarationName(&ctx.Idents.get(name)), - decl->getTemplateParameters(), pattern); - pattern->setDescribedClassTemplate(mirror); - mirror->setAccess(clang::AS_public); - substs.try_emplace(decl->getCanonicalDecl(), mirror); - - clang::QualType base_t = getTemplateIdType( - decl, decl->getTemplateParameters()->getInjectedTemplateArgs(ctx)); - assert(!base_t.isNull() && "Failed building mirror base"); - - pattern->startDefinition(); - clang::CXXBaseSpecifier base(clang::SourceRange(loc_, loc_), false, true, - clang::AS_public, - ctx.getTrivialTypeSourceInfo(base_t, loc_), - /*EllipsisLoc=*/clang::SourceLocation()); - const clang::CXXBaseSpecifier *bases[] = {&base}; - pattern->setBases(bases, 1); - - for (auto *member : decl->getTemplatedDecl()->decls()) { - if (const auto *td = llvm::dyn_cast(member)) { - if (auto *replacement = findMatch(substs, td->getUnderlyingType())) { - auto *copy = clang::TypedefDecl::Create( - ctx, pattern, loc_, loc_, td->getIdentifier(), replacement); - copy->setAccess(clang::AS_public); - pattern->addDecl(copy); - } - } else if (auto *tdecl = - llvm::dyn_cast(member)) { - clang::Sema::ContextRAII savedContext(*sema_, pattern); - createInheritingTemplate(tdecl->getName(), tdecl, substs); - } - } - - pattern->completeDefinition(); - sema_->CurContext->addDecl(mirror); - return mirror; - } - - clang::QualType createMirrorType(llvm::StringRef name, clang::QualType hint) { - clang::ASTContext &ctx = sema_->Context; - forceCompleteDefinition(hint); - - const auto *hdecl = hint->getAsCXXRecordDecl(); - assert(hdecl && "Failed resolving hint record declaration"); - assert(hdecl->isCompleteDefinition() && "Incomplete hint"); - - const auto *hspec = - llvm::dyn_cast(hdecl); - if (!hspec) { - // if it is not a template specialization inheriting from it suffices - clang::RecordDecl *rdecl = createRecordDecl(name, hint); - return ctx.getTagType(clang::ElaboratedTypeKeyword::None, - rdecl->getQualifier(), rdecl, false); - } - - MirrorMap substs; - auto *mirror = - createInheritingTemplate(name, hspec->getSpecializedTemplate(), substs); - clang::QualType spec = - getTemplateIdType(mirror, hspec->getTemplateArgs().asArray()); - assert(!spec.isNull() && spec->getAsCXXRecordDecl()); - - // required to print Tn instead of Tn - clang::NamespaceDecl *ns = createNamespaceDecl(); - auto *alias = - clang::TypeAliasDecl::Create(ctx, ns, loc_, loc_, &ctx.Idents.get(name), - ctx.getTrivialTypeSourceInfo(spec, loc_)); - ns->addDecl(alias); - - clang::QualType alias_t = ctx.getTypedefType( - clang::ElaboratedTypeKeyword::None, std::nullopt, alias); - spec->getAsCXXRecordDecl()->addAttr( - clang::PreferredNameAttr::CreateImplicit( - ctx, ctx.getTrivialTypeSourceInfo(alias_t, loc_))); - - return ctx.getCanonicalType(spec); - } - - clang::QualType getSubstType(const clang::Sema::InstantiatingTemplate &Inst, - clang::QualType type, - llvm::ArrayRef args) { - assert(!Inst.isInvalid() && "Invalid instantiation context"); - clang::MultiLevelTemplateArgumentList mtal; - mtal.setKind(clang::TemplateSubstitutionKind::Rewrite); - mtal.addOuterTemplateArguments(args); - - clang::TypeSourceInfo *tsi = - sema_->SubstType(sema_->Context.getTrivialTypeSourceInfo(type), mtal, - loc_, clang::DeclarationName()); - assert(tsi && "Template argument type instantiation failed"); - return tsi->getType(); - } - - clang::QualType - getDefaultArg(clang::TemplateDecl *decl, - const clang::TemplateTypeParmDecl *parm, - llvm::ArrayRef currentArgs) { - clang::QualType type = parm->getDefaultArgument().getArgument().getAsType(); - if (!type->isDependentType()) { - return type; - } - - const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); - return getSubstType(Inst, type, currentArgs); - } - - clang::VarDecl *createVarDecl(clang::QualType type, llvm::StringRef name, - clang::StorageClass sclass = clang::SC_None) { - clang::ASTContext &ctx = sema_->Context; - clang::VarDecl *decl = clang::VarDecl::Create( - ctx, sema_->CurContext, loc_, loc_, &ctx.Idents.get(name), - type.getNonReferenceType(), nullptr, sclass); - sema_->CurContext->addDecl(decl); - decl->markUsed(ctx); - return decl; - } - - clang::DeclRefExpr *createDeclRefExpr(clang::VarDecl *decl) { - const clang::DeclarationNameInfo nameInfo(decl->getDeclName(), loc_); - return sema_->BuildDeclRefExpr(decl, decl->getType(), clang::VK_LValue, - nameInfo, decl->getQualifierLoc()); - } - - clang::DeclRefExpr *createConstexprDeclRefExpr(clang::QualType type, - llvm::StringRef name) { - clang::VarDecl *decl = createVarDecl(type, name, clang::SC_Static); - decl->setConstexpr(true); - - clang::Expr *init; - clang::ASTContext &ctx = sema_->Context; - if (type->isIntegerType()) { - init = clang::IntegerLiteral::Create( - ctx, llvm::APInt(ctx.getIntWidth(type), 1), type, loc_); - } else { - init = new (ctx) clang::ImplicitValueInitExpr(type); - } - decl->setInit(init); - return createDeclRefExpr(decl); - } - - clang::OpaqueValueExpr *createOpaqueValueExpr(clang::QualType type) { - return new (sema_->Context) clang::OpaqueValueExpr( - loc_, type.getNonReferenceType(), - type->isRValueReferenceType() ? clang::VK_XValue : clang::VK_LValue); - } - - void - createTemplateArguments(clang::TemplateDecl *decl, - llvm::SmallVectorImpl &out) { - for (clang::NamedDecl *param : *decl->getTemplateParameters()) { - if (const auto *ttp = - llvm::dyn_cast(param)) { - clang::QualType type; - if (param->isTemplateParameterPack()) { - out.emplace_back(clang::TemplateArgument::getEmptyPack()); - continue; - } - if (ttp->hasDefaultArgument()) { - clang::QualType hint = getDefaultArg(decl, ttp, out); - assert(!hint.isNull() && "Failed retrieving type hint"); - type = createMirrorType(param->getName(), hint); - } else { - clang::RecordDecl *rdecl = createRecordDecl(param->getName()); - type = sema_->Context.getTagType(clang::ElaboratedTypeKeyword::None, - rdecl->getQualifier(), rdecl, false); - } - assert(!type.isNull() && "Template type argument creation failed"); - out.emplace_back(type); - } else if (const auto *nttp = - llvm::dyn_cast(param)) { - clang::QualType type = nttp->getType(); - if (type->isDependentType()) { - const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); - type = getSubstType(Inst, type, out); - } - clang::DeclRefExpr *var = - createConstexprDeclRefExpr(type, param->getName()); - out.emplace_back(var, true); - } else { - assert(0 && "Unsupported template param kind"); - } - } - } - - clang::FunctionDecl *instantiateRuleDecl(clang::FunctionTemplateDecl *decl) { - llvm::SmallVector args; - createTemplateArguments(decl, args); - return sema_->InstantiateFunctionDeclaration( - decl, clang::TemplateArgumentList::CreateCopy(sema_->Context, args), - loc_); - } - - clang::FunctionDecl *createCandidate( - clang::NamedDecl *decl, llvm::ArrayRef callArgs, - clang::TemplateArgumentListInfo *explicitArgs = nullptr, - clang::QualType obj_t = clang::QualType(), - clang::Expr::Classification eclass = clang::Expr::Classification()) { - if (auto *tdecl = llvm::dyn_cast(decl)) { - if (auto *fdecl = deduceTemplateArguments(tdecl, callArgs, obj_t, eclass, - explicitArgs)) { - return fdecl; - } - return nullptr; - } - return llvm::dyn_cast(decl); - } - - clang::CXXRecordDecl *resolveCXXRecordDecl(clang::QualType obj_t) { - obj_t = obj_t.getCanonicalType(); - while (obj_t->isPointerOrReferenceType()) { - obj_t = obj_t->getPointeeType(); - } - - forceCompleteDefinition(obj_t); - if (auto *rdecl = obj_t->getAsCXXRecordDecl()) { - return rdecl->getDefinition(); - } - return nullptr; - } - - void regularNameLookup(llvm::ArrayRef callArgs, - clang::TemplateArgumentListInfo *explicitTArgs, - clang::DeclarationName &name, - clang::OverloadCandidateSet &candidates) { - clang::LookupResult decls(*sema_, name, loc_, - clang::Sema::LookupOrdinaryName); - if (clang::NamespaceDecl *std_ns = sema_->getStdNamespace()) { - sema_->LookupQualifiedName(decls, std_ns); - } - if (decls.empty()) { - decls.clear(); - sema_->LookupQualifiedName(decls, - sema_->Context.getTranslationUnitDecl()); - } - for (auto *ndecl : decls) { - if (auto *candidate = createCandidate(ndecl, callArgs, explicitTArgs)) { - sema_->AddOverloadCandidate( - candidate, clang::DeclAccessPair::make(candidate, clang::AS_public), - callArgs, candidates, false); - } - } - - for (const auto *arg : callArgs) { - if (auto *rdecl = resolveCXXRecordDecl(arg->getType())) { - for (auto *frdecl : rdecl->friends()) { - auto *fd = frdecl->getFriendDecl(); - if (!fd) { - continue; - } - - if (auto *ndecl = llvm::dyn_cast(fd); - ndecl && ndecl->getDeclName() == name) { - if (auto *candidate = - createCandidate(ndecl, callArgs, explicitTArgs)) { - sema_->AddOverloadCandidate( - candidate, - clang::DeclAccessPair::make(candidate, clang::AS_public), - callArgs, candidates, false); - } - } - } - } - } - } - - void cxxMethodNameLookup(clang::QualType obj_t, - llvm::ArrayRef callArgs, - clang::TemplateArgumentListInfo *explicitTArgs, - clang::DeclarationName &name, - clang::OverloadCandidateSet &candidates) { - clang::CXXRecordDecl *rdecl = resolveCXXRecordDecl(obj_t); - assert(rdecl && "Failed fetching record decl"); - clang::LookupResult members(*sema_, name, loc_, - clang::Sema::LookupMemberName); - sema_->LookupQualifiedName(members, rdecl); - - auto eclass = clang::Expr::Classification::makeSimpleLValue(); - for (auto *ndecl : members) { - if (auto *candidate = - createCandidate(ndecl, callArgs, explicitTArgs, obj_t, eclass)) { - sema_->AddMethodCandidate( - clang::DeclAccessPair::make(candidate, clang::AS_public), obj_t, - eclass, callArgs, candidates); - } - } - } - - void cxxConstructorNameLookup(clang::QualType obj_t, - llvm::ArrayRef callArgs, - clang::OverloadCandidateSet &candidates) { - clang::CXXRecordDecl *rdecl = resolveCXXRecordDecl(obj_t); - assert(rdecl && "Failed fetching record decl"); - clang::DeclContextLookupResult ctors = sema_->LookupConstructors(rdecl); - - for (auto *ndecl : ctors) { - if (auto *candidate = createCandidate(ndecl, callArgs)) { - sema_->AddOverloadCandidate( - candidate, clang::DeclAccessPair::make(candidate, clang::AS_public), - callArgs, candidates, false); - } - } - } - - void adlLookup(llvm::ArrayRef callArgs, - clang::DeclarationName &name, - clang::OverloadCandidateSet &candidates) { - clang::ADLResult adl; - sema_->ArgumentDependentLookup(name, loc_, callArgs, adl); - - for (auto *ndecl : adl) { - if (auto *candidate = createCandidate(ndecl, callArgs)) { - sema_->AddOverloadCandidate( - candidate, clang::DeclAccessPair::make(candidate, clang::AS_public), - callArgs, candidates, false); - } - } - } - - clang::FunctionDecl *lookupCalledDecl(clang::FunctionTemplateDecl *decl, - LookupInfo &lookup, - clang::FunctionDecl **rule_out) { - clang::NamespaceDecl *ns = createNamespaceDecl(); - clang::Sema::ContextRAII savedContext(*sema_, ns); - clang::FunctionDecl *rule = instantiateRuleDecl(decl); - assert(rule && "Rule instantiation failed"); - if (rule_out) { - *rule_out = rule; - } - llvm::ArrayRef parms = rule->parameters(); - auto csk = lookup.name.getNameKind() == - clang::DeclarationName::NameKind::CXXOperatorName - ? clang::OverloadCandidateSet::CSK_Operator - : clang::OverloadCandidateSet::CSK_Normal; - - llvm::SmallVector callArgs; - for (const auto *parm : parms) { - clang::QualType parm_t = parm->getType(); - forceCompleteDefinition(parm_t); - callArgs.emplace_back(createOpaqueValueExpr(parm_t)); - } - - llvm::ArrayRef ruleTArgs = - rule->getTemplateSpecializationArgs()->asArray(); - clang::TemplateArgumentListInfo explicitTArgs; - - { - const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); - assert(!Inst.isInvalid() && "Invalid instantiation context"); - for (const auto &argloc : lookup.explicitArgs) { - const auto &arg = argloc.getArgument(); - if (!arg.isDependent()) { - explicitTArgs.addArgument(argloc); - continue; - } - - clang::TemplateArgument inst; - if (arg.getKind() == clang::TemplateArgument::Type) { - inst = clang::TemplateArgument( - getSubstType(Inst, arg.getAsType(), ruleTArgs)); - } else if (arg.getKind() == clang::TemplateArgument::Expression) { - if (auto *expr = - llvm::dyn_cast(arg.getAsExpr())) { - const auto *nttp = - llvm::dyn_cast(expr->getDecl()); - assert(nttp && "Unexpected decl in expr"); - inst = ruleTArgs[nttp->getIndex()]; - } else { - assert(0 && "Unsupported explicit template argument expression"); - } - } else { - assert(0 && "Unsupported explicit template argument kind"); - } - - explicitTArgs.addArgument( - sema_->getTrivialTemplateArgumentLoc(inst, {}, loc_)); - } - } - - clang::DeclarationName name = lookup.name; - if (clang::QualType nameType = name.getCXXNameType(); - !nameType.isNull() && nameType->isDependentType()) { - const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); - assert(!Inst.isInvalid() && "Invalid instantiation context"); - clang::MultiLevelTemplateArgumentList mtal; - mtal.setKind(clang::TemplateSubstitutionKind::Rewrite); - mtal.addOuterTemplateArguments(ruleTArgs); - name = sema_->SubstDeclarationNameInfo({name, loc_}, mtal).getName(); - } - - clang::OverloadCandidateSet candidates(loc_, csk); - switch (lookup.kind) { - case LookupKind::RegularName: - regularNameLookup(callArgs, &explicitTArgs, name, candidates); - break; - case LookupKind::CXXMethodName: { - llvm::ArrayRef margs = callArgs; - cxxMethodNameLookup(margs.front()->getType().getNonReferenceType(), - margs.drop_front(), &explicitTArgs, name, candidates); - break; - } - case LookupKind::CXXConstructorName: - cxxConstructorNameLookup(rule->getReturnType(), callArgs, candidates); - break; - case LookupKind::ADL: - regularNameLookup(callArgs, &explicitTArgs, name, candidates); - adlLookup(callArgs, name, candidates); - break; - } - - clang::OverloadCandidateSet::iterator best; - switch (candidates.BestViableFunction(*sema_, loc_, best)) { - case clang::OverloadingResult::OR_Success: - return best->Function; - case clang::OverloadingResult::OR_Ambiguous: - for (auto &candidate : candidates) { - if (candidate.Viable) { - return candidate.Function; - } - } - break; - case clang::OverloadingResult::OR_No_Viable_Function: - llvm::errs() << "No viable function\n"; - break; - case clang::OverloadingResult::OR_Deleted: - llvm::errs() << "Deleted function selected\n"; - break; - } - - assert(0 && "Rule resolution failed"); - return nullptr; - } - - clang::NamedDecl *lookupMemberAccess(clang::FunctionTemplateDecl *decl, - clang::DeclarationName name) { - clang::NamespaceDecl *ns = createNamespaceDecl(); - clang::Sema::ContextRAII savedContext(*sema_, ns); - clang::FunctionDecl *rule = instantiateRuleDecl(decl); - assert(rule && "Rule instantiation failed"); - clang::CXXRecordDecl *rdecl = - resolveCXXRecordDecl(rule->getParamDecl(0)->getType()); - assert(rdecl && "Failed fetching record decl"); - clang::LookupResult members(*sema_, name, loc_, - clang::Sema::LookupMemberName); - sema_->LookupQualifiedName(members, rdecl); - assert(!members.empty() && "Rule resolution failed"); - return members.getRepresentativeDecl(); - } - - clang::MemberExpr * - lookupArrowAccess(clang::FunctionTemplateDecl *decl, - const clang::DeclarationNameInfo &nameInfo, - clang::NestedNameSpecifierLoc nns) { - clang::NamespaceDecl *ns = createNamespaceDecl(); - clang::Sema::ContextRAII savedContext(*sema_, ns); - clang::FunctionDecl *rule = instantiateRuleDecl(decl); - assert(rule && "Rule instantiation failed"); - - clang::Expr *obj = createOpaqueValueExpr( - rule->getParamDecl(0)->getType().getNonReferenceType()); - auto arrow = - sema_->BuildOverloadedArrowExpr(sema_->getCurScope(), obj, loc_); - assert(arrow.isUsable() && "Overloaded arrow operator not found"); - - auto *base = arrow.getAs(); - assert(base && "Unexpected base type"); - - clang::CXXRecordDecl *rdecl = - resolveCXXRecordDecl(base->getType()->getPointeeType()); - assert(rdecl && "Failed fetching record decl"); - - clang::LookupResult members(*sema_, nameInfo.getName(), loc_, - clang::Sema::LookupMemberName); - sema_->LookupQualifiedName(members, rdecl); - for (auto *ndecl : members) { - if (auto *vdecl = llvm::dyn_cast(ndecl)) { - clang::MemberExpr *access = sema_->BuildMemberExpr( - base, true, loc_, nns, loc_, vdecl, - clang::DeclAccessPair::make(vdecl, clang::AS_public), false, - nameInfo, vdecl->getType(), clang::VK_LValue, clang::OK_Ordinary); - assert(access && "Rule resolution failed"); - return access; - } - } - assert(0 && "Rule resolution failed"); - return nullptr; - } - - clang::QualType lookupType(clang::TypeAliasTemplateDecl *decl) { - clang::NamespaceDecl *ns = createNamespaceDecl(); - clang::Sema::ContextRAII savedContext(*sema_, ns); - - llvm::SmallVector args; - createTemplateArguments(decl, args); - - clang::MultiLevelTemplateArgumentList mtal; - mtal.setKind(clang::TemplateSubstitutionKind::Rewrite); - mtal.addOuterTemplateArguments(args); - - clang::Sema::InstantiatingTemplate TypeInst(*sema_, loc_, decl, args); - assert(!TypeInst.isInvalid() && "Invalid instantiation context"); - - clang::TypeSourceInfo *tsi = - sema_->SubstType(decl->getTemplatedDecl()->getTypeSourceInfo(), mtal, - loc_, clang::DeclarationName()); - assert(tsi && "Rule resolution failed"); - return tsi->getType(); - } -}; - -class ActionFactory : public clang::tooling::FrontendActionFactory { -public: - explicit ActionFactory(llvm::json::Object &out) : cb_(out) { - using namespace clang::ast_matchers; - finder_.addMatcher( - returnStmt( - isExpansionInMainFile(), - hasReturnValue(ignoringImplicit(ignoringParenImpCasts(anyOf( - callExpr().bind("fcall"), cxxConstructExpr().bind("ctor"), - cxxFunctionalCastExpr(has(ignoringImplicit( - ignoringParenImpCasts(cxxConstructExpr().bind("ctor"))))), - memberExpr(hasDeclaration(fieldDecl())).bind("muse"), - unresolvedMemberExpr().bind("umuse"), - declRefExpr(to(anyOf(enumConstantDecl().bind("enum_val"), - decl(unless(parmVarDecl())).bind("decl")))) - .bind("declref"), - unaryOperator(hasUnaryOperand( - declRefExpr(to(decl(unless(parmVarDecl())))))) - .bind("udeclref"), - cxxDependentScopeMemberExpr().bind("dsme"), - cxxUnresolvedConstructExpr().bind("uctor"), - integerLiteral().bind("macro_int"))))), - hasAncestor(functionDecl(isDefinition(), - matchesName("(^|::)f[0-9]+$"), - isExpansionInMainFile()) - .bind("func"))), - &cb_); - - finder_.addMatcher( - typedefNameDecl(matchesName("(^|::)t[0-9]+$"), isExpansionInMainFile()) - .bind("tvar"), - &cb_); - - finder_.addMatcher( - varDecl(matchesName("(^|::)f[0-9]+$"), isExpansionInMainFile(), - hasInitializer(ignoringImplicit(ignoringParenImpCasts( - declRefExpr(to(functionDecl().bind("fn"))))))) - .bind("fvar"), - &cb_); - - finder_.addMatcher(functionDecl(isDefinition(), - matchesName("(^|::)f[0-9]+$"), - isExpansionInMainFile()) - .bind("validate_func"), - &cb_); - } - - std::unique_ptr create() override { - class ASTConsumer : public clang::ASTConsumer { - public: - explicit ASTConsumer(std::unique_ptr AC, - clang::CompilerInstance &CI, Callback *CB) - : AC_(std::move(AC)), CI_(&CI), CB_(CB) {} - - void HandleTranslationUnit(clang::ASTContext &ctx) override { - auto &DE = CI_->getDiagnostics(); - if (DE.hasErrorOccurred()) { - std::exit(EXIT_FAILURE); - } - DE.setSuppressAllDiagnostics(true); - DE.setClient(new clang::IgnoringDiagConsumer(), true); - CB_->init(CI_->getSema()); - AC_->HandleTranslationUnit(ctx); - } - - private: - std::unique_ptr AC_; - clang::CompilerInstance *CI_; - Callback *CB_; - }; - - class Wrapped : public clang::ASTFrontendAction { - clang::ast_matchers::MatchFinder &F_; - Callback *CB_; - - public: - explicit Wrapped(clang::ast_matchers::MatchFinder &MF, Callback &CB) - : F_(MF), CB_(&CB) {} - - std::unique_ptr - CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef) override { - return std::make_unique(F_.newASTConsumer(), CI, CB_); - } - }; - return std::make_unique(finder_, cb_); - } - -private: - clang::ast_matchers::MatchFinder finder_; - Callback cb_; -}; - -void Extract(const std::filesystem::path &src_path, llvm::json::Object &out, - llvm::ArrayRef cxx_flags) { - bool is_c = src_path.extension() == ".c"; - auto flags = getPlatformClangBeginFlags(); - flags.push_back("-isystem" + src_path.parent_path().string()); - if (!is_c) { - flags.insert(flags.end(), cxx_flags.begin(), cxx_flags.end()); - } - auto end_flags = getPlatformClangEndFlags(); - flags.insert(flags.end(), end_flags.begin(), end_flags.end()); - auto code = llvm::MemoryBuffer::getFile(src_path.string()); - if (!code) { - llvm::errs() << "ERROR: cannot read " << src_path.string() << '\n'; - std::exit(EXIT_FAILURE); - } - ActionFactory factory(out); - clang::tooling::runToolOnCodeWithArgs( - factory.create(), (*code)->getBuffer(), flags, src_path.string(), - is_c ? CLANG_C_COMPILER : CLANG_CXX_COMPILER); -} - -} // namespace cpp2rust - -namespace { - -llvm::cl::OptionCategory cat("cpp-rule-preprocessor options"); - -llvm::cl::opt - SrcDir("dir", - llvm::cl::desc("Path to a rule directory containing src.c and/or " - "src.cpp."), - llvm::cl::value_desc("rule-dir"), llvm::cl::Required, - llvm::cl::cat(cat)); - -llvm::cl::opt - OutPath("out", llvm::cl::desc("Path of the ir_src.json file to write."), - llvm::cl::value_desc("out.json"), llvm::cl::Required, - llvm::cl::cat(cat)); - -llvm::cl::list CXXFlags("cxxflags", - llvm::cl::desc("Additional CXXFLAGS"), - llvm::cl::value_desc("cxxflags"), - llvm::cl::ZeroOrMore, llvm::cl::cat(cat)); - -} // namespace - -int main(int argc, char *argv[]) { - llvm::cl::HideUnrelatedOptions(cat); - llvm::cl::ParseCommandLineOptions(argc, argv); - - llvm::SmallVector cxx_flags(CXXFlags.begin(), - CXXFlags.end()); - fs::path dir = SrcDir.getValue(); - llvm::json::Object root; - for (const char *name : {"src.c", "src.cpp"}) { - auto path = dir / name; - if (!fs::exists(path)) { - continue; - } - llvm::errs() << "Preprocessing " << path.string() << '\n'; - llvm::json::Object file_root; - cpp2rust::Extract(path, file_root, cxx_flags); - for (auto &[k, v] : file_root) { - if (!root.try_emplace(k, std::move(v)).second) { - llvm::errs() << "ERROR: rule name " << k.str() - << " defined in multiple files in " << dir.string() - << '\n'; - return EXIT_FAILURE; - } - } - } - - fs::path out_path = OutPath.getValue(); - std::error_code ec; - llvm::raw_fd_ostream out(out_path.string(), ec); - if (ec) { - llvm::errs() << "ERROR: failed to open " << out_path.string() << ": " - << ec.message() << '\n'; - return EXIT_FAILURE; - } - out << llvm::formatv("{0:2}", llvm::json::Value(std::move(root))) << '\n'; - return EXIT_SUCCESS; -} diff --git a/docs/src/rules/format.md b/docs/src/rules/format.md index 199c4481f..063546062 100644 --- a/docs/src/rules/format.md +++ b/docs/src/rules/format.md @@ -5,6 +5,8 @@ library it covers (`rules/unistd`, `rules/vector`, `rules/string`, ...). It contains: - `src.cpp` and/or `src.c`: the C++ (or C) side of each rule. +- `src_cxxNN.cpp` (optional): C++ rules that exist from C++NN on, for example + `src_cxx17.cpp`; see below. - `tgt_unsafe.rs`: the Rust targets for the unsafe model. - `tgt_refcount.rs`: the Rust targets for the reference counting model (optional, see below). @@ -141,6 +143,21 @@ for the C++ `__builtin_mul_overflow` (returning `bool`) while `src.c` defines `f12`/`f13` for the C version (returning `int`); their Rust bodies are identical. +## Standard versions + +`src.cpp` is parsed as C++11 and its rules are available to every C++ +translation unit. A rule that needs a later standard goes into `src_cxxNN.cpp`, +which is parsed as C++NN and whose rules are only loaded into translation units +compiled as C++NN or later: + +```cpp +// rules/new/src_cxx17.cpp +template T1 *f5(T1 *a0) { return std::launder(a0); } +``` + +Rule names are shared across all the files of a module. Platform `#if`s are fine +in any file; `#if __cplusplus` is not needed. + ## The `rules` crate The whole `rules/` tree is a single Rust crate. `rules/build.rs` walks the tree, diff --git a/rules/algorithm/src.cpp b/rules/algorithm/src.cpp index 18476d34e..40b020c3f 100644 --- a/rules/algorithm/src.cpp +++ b/rules/algorithm/src.cpp @@ -4,9 +4,13 @@ #include #include #include +#include #include +#include #include +#include "../common/traits.h" + template void f1(T1 first, T1 last) { return std::sort(first, last); } @@ -29,15 +33,17 @@ template T1 *f8(T1 *first, T1 *last) { template void f9(T1 &a0, T1 &a1) { return std::swap(a0, a1); } -template -typename std::vector::iterator f10(typename std::vector::iterator a0, - typename std::vector::iterator a1) { +template , + typename T2 = AllocatorOf>, + typename = SameAs::iterator>> +typename std::vector::iterator f10(It a0, It a1) { return std::unique(a0, a1); } -template -void f12(typename std::vector::iterator a0, - typename std::vector::iterator a1, const T2 &a2) { +template , + typename T3 = AllocatorOf>, + typename = SameAs::iterator>> +void f12(It a0, It a1, const T2 &a2) { return std::fill(a0, a1, a2); } diff --git a/rules/array/src.cpp b/rules/array/src.cpp index b816fa2c5..ff2f6b4cd 100644 --- a/rules/array/src.cpp +++ b/rules/array/src.cpp @@ -29,7 +29,7 @@ std::array f4(std::array &&o) { template std::array &f5(std::array &dst, std::array &&src) { - return dst.operator=(std::move(src)); + return dst = std::move(src); } template @@ -39,5 +39,5 @@ std::array f6(const std::array &o) { template std::array &f7(std::array &dst, const std::array &src) { - return dst.operator=(src); + return dst = src; } diff --git a/rules/brotli/src.c b/rules/brotli/src.c index f54b93661..fab586d5a 100644 --- a/rules/brotli/src.c +++ b/rules/brotli/src.c @@ -10,7 +10,7 @@ typedef struct BrotliDecoderStateStruct *t3; typedef const struct BrotliDecoderStateStruct *t4; typedef BrotliDecoderErrorCode t5; -BrotliEncoderMode f1() { return BROTLI_MODE_FONT; } +BrotliEncoderMode f1(void) { return BROTLI_MODE_FONT; } BROTLI_BOOL f2(int quality, int lgwin, BrotliEncoderMode mode, size_t input_size, @@ -21,9 +21,9 @@ BROTLI_BOOL f2(int quality, int lgwin, BrotliEncoderMode mode, encoded_size, encoded_buffer); } -BrotliEncoderMode f3() { return BROTLI_MODE_TEXT; } +BrotliEncoderMode f3(void) { return BROTLI_MODE_TEXT; } -BrotliDecoderResult f4() { return BROTLI_DECODER_RESULT_SUCCESS; } +BrotliDecoderResult f4(void) { return BROTLI_DECODER_RESULT_SUCCESS; } BrotliDecoderResult f5(size_t encoded_size, @@ -54,57 +54,77 @@ const uint8_t *f9(BrotliDecoderState *state, size_t *size) { return BrotliDecoderTakeOutput(state, size); } -BrotliDecoderResult f10() { return BROTLI_DECODER_RESULT_ERROR; } +BrotliDecoderResult f10(void) { return BROTLI_DECODER_RESULT_ERROR; } -BrotliDecoderResult f11() { return BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; } +BrotliDecoderResult f11(void) { + return BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; +} -BrotliEncoderMode f12() { return BROTLI_MODE_GENERIC; } +BrotliEncoderMode f12(void) { return BROTLI_MODE_GENERIC; } -BrotliDecoderResult f13() { return BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; } +BrotliDecoderResult f13(void) { return BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT; } -BrotliDecoderErrorCode f14() { +BrotliDecoderErrorCode f14(void) { return BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE; } -BrotliDecoderErrorCode f15() { +BrotliDecoderErrorCode f15(void) { return BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE; } -BrotliDecoderErrorCode f16() { +BrotliDecoderErrorCode f16(void) { return BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET; } -BrotliDecoderErrorCode f17() { +BrotliDecoderErrorCode f17(void) { return BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME; } -BrotliDecoderErrorCode f18() { return BROTLI_DECODER_ERROR_FORMAT_CL_SPACE; } -BrotliDecoderErrorCode f19() { +BrotliDecoderErrorCode f18(void) { + return BROTLI_DECODER_ERROR_FORMAT_CL_SPACE; +} +BrotliDecoderErrorCode f19(void) { return BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE; } -BrotliDecoderErrorCode f20() { +BrotliDecoderErrorCode f20(void) { return BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT; } -BrotliDecoderErrorCode f21() { +BrotliDecoderErrorCode f21(void) { return BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1; } -BrotliDecoderErrorCode f22() { +BrotliDecoderErrorCode f22(void) { return BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2; } -BrotliDecoderErrorCode f23() { return BROTLI_DECODER_ERROR_FORMAT_TRANSFORM; } -BrotliDecoderErrorCode f24() { return BROTLI_DECODER_ERROR_FORMAT_DICTIONARY; } -BrotliDecoderErrorCode f25() { return BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS; } -BrotliDecoderErrorCode f26() { return BROTLI_DECODER_ERROR_FORMAT_PADDING_1; } -BrotliDecoderErrorCode f27() { return BROTLI_DECODER_ERROR_FORMAT_PADDING_2; } -BrotliDecoderErrorCode f28() { return BROTLI_DECODER_ERROR_INVALID_ARGUMENTS; } -BrotliDecoderErrorCode f29() { +BrotliDecoderErrorCode f23(void) { + return BROTLI_DECODER_ERROR_FORMAT_TRANSFORM; +} +BrotliDecoderErrorCode f24(void) { + return BROTLI_DECODER_ERROR_FORMAT_DICTIONARY; +} +BrotliDecoderErrorCode f25(void) { + return BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS; +} +BrotliDecoderErrorCode f26(void) { + return BROTLI_DECODER_ERROR_FORMAT_PADDING_1; +} +BrotliDecoderErrorCode f27(void) { + return BROTLI_DECODER_ERROR_FORMAT_PADDING_2; +} +BrotliDecoderErrorCode f28(void) { + return BROTLI_DECODER_ERROR_INVALID_ARGUMENTS; +} +BrotliDecoderErrorCode f29(void) { return BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES; } -BrotliDecoderErrorCode f30() { return BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS; } -BrotliDecoderErrorCode f31() { return BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP; } -BrotliDecoderErrorCode f32() { +BrotliDecoderErrorCode f30(void) { + return BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS; +} +BrotliDecoderErrorCode f31(void) { + return BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP; +} +BrotliDecoderErrorCode f32(void) { return BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1; } -BrotliDecoderErrorCode f33() { +BrotliDecoderErrorCode f33(void) { return BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2; } -BrotliDecoderErrorCode f34() { +BrotliDecoderErrorCode f34(void) { return BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES; } @@ -112,4 +132,4 @@ BrotliDecoderErrorCode f35(const BrotliDecoderState *state) { return BrotliDecoderGetErrorCode(state); } -uint32_t f36() { return BrotliDecoderVersion(); } +uint32_t f36(void) { return BrotliDecoderVersion(); } diff --git a/rules/builtin/src.c b/rules/builtin/src.c index 4dbdf60fb..7dc2baacf 100644 --- a/rules/builtin/src.c +++ b/rules/builtin/src.c @@ -35,10 +35,10 @@ void *f14(void *dst, const void *src, size_t n) { return __builtin_memcpy(dst, src, n); } -void f15() { return __builtin_abort(); } +void f15(void) { return __builtin_abort(); } int f16(unsigned long x) { return __builtin_clzl(x); } -float f17() { return __builtin_inff(); } +float f17(void) { return __builtin_inff(); } float f18(const char *tagp) { return __builtin_nanf(tagp); } diff --git a/rules/builtin_types/cxxflags b/rules/builtin_types/cxxflags deleted file mode 100644 index e23b2aef6..000000000 --- a/rules/builtin_types/cxxflags +++ /dev/null @@ -1 +0,0 @@ --std=c++20 diff --git a/rules/builtin_types/src.c b/rules/builtin_types/src.c index fa25dba68..8f0a5546c 100644 --- a/rules/builtin_types/src.c +++ b/rules/builtin_types/src.c @@ -1,29 +1,27 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -#define BUILTIN_TYPE(T, N0, N1, N2, N3, N4) \ - typedef T N0; \ - typedef T *N1; \ - typedef const T *N2; \ - typedef volatile T *N3; \ - typedef const volatile T *N4; - -BUILTIN_TYPE(char, t20, t21, t22, t23, t24) -BUILTIN_TYPE(signed char, t30, t31, t32, t33, t34) -BUILTIN_TYPE(unsigned char, t40, t41, t42, t43, t44) -BUILTIN_TYPE(short, t60, t61, t62, t63, t64) -BUILTIN_TYPE(unsigned short, t70, t71, t72, t73, t74) -BUILTIN_TYPE(int, t90, t91, t92, t93, t94) -BUILTIN_TYPE(unsigned int, t100, t101, t102, t103, t104) -BUILTIN_TYPE(float, t130, t131, t132, t133, t134) -BUILTIN_TYPE(long, t140, t141, t142, t143, t144) -BUILTIN_TYPE(unsigned long, t150, t151, t152, t153, t154) -BUILTIN_TYPE(long long, t160, t161, t162, t163, t164) -BUILTIN_TYPE(unsigned long long, t170, t171, t172, t173, t174) -BUILTIN_TYPE(double, t180, t181, t182, t183, t184) -BUILTIN_TYPE(long double, t190, t191, t192, t193, t194) -BUILTIN_TYPE(__int128, t200, t201, t202, t203, t204) -BUILTIN_TYPE(unsigned __int128, t210, t211, t212, t213, t214) -BUILTIN_TYPE(void, t220, t221, t222, t223, t224) - -#undef BUILTIN_TYPE +#ifndef __cplusplus +typedef _Bool t10; +#endif +typedef char t20; +typedef signed char t30; +typedef unsigned char t40; +typedef short t60; +typedef unsigned short t70; +typedef int t90; +typedef unsigned int t100; +typedef float t130; +typedef long t140; +typedef unsigned long t150; +typedef long long t160; +typedef unsigned long long t170; +typedef double t180; +typedef long double t190; +typedef __int128 t200; +typedef unsigned __int128 t210; +typedef void t220; +typedef void *t221; +typedef const void *t222; +typedef volatile void *t223; +typedef const volatile void *t224; diff --git a/rules/builtin_types/src.cpp b/rules/builtin_types/src.cpp index ff114783c..7ea9b8bc7 100644 --- a/rules/builtin_types/src.cpp +++ b/rules/builtin_types/src.cpp @@ -3,21 +3,9 @@ #include -#define BUILTIN_TYPE(N, T) \ - typedef T t##N##0; \ - typedef T *t##N##1; \ - typedef const T *t##N##2; \ - typedef volatile T *t##N##3; \ - typedef const volatile T *t##N##4; - typedef decltype(nullptr) t0; -BUILTIN_TYPE(1, bool) -#if __cplusplus >= 202002L -BUILTIN_TYPE(5, char8_t) -#endif -BUILTIN_TYPE(8, char16_t) -BUILTIN_TYPE(11, wchar_t) -BUILTIN_TYPE(12, char32_t) - -#undef BUILTIN_TYPE +typedef bool t10; +typedef char16_t t80; +typedef wchar_t t110; +typedef char32_t t120; diff --git a/rules/deque_cxx11/tgt_unsafe.rs b/rules/builtin_types/src_cxx20.cpp similarity index 55% rename from rules/deque_cxx11/tgt_unsafe.rs rename to rules/builtin_types/src_cxx20.cpp index c41e321a0..9dcede19b 100644 --- a/rules/deque_cxx11/tgt_unsafe.rs +++ b/rules/builtin_types/src_cxx20.cpp @@ -1,7 +1,4 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -unsafe fn f1(a0: &mut Vec, init: T1) { - let __init = init; - a0.push(__init) -} +typedef char8_t t50; diff --git a/rules/builtin_types/tgt_refcount.rs b/rules/builtin_types/tgt_refcount.rs index 66126c37e..49056e0d1 100644 --- a/rules/builtin_types/tgt_refcount.rs +++ b/rules/builtin_types/tgt_refcount.rs @@ -7,346 +7,10 @@ fn t0() -> AnyPtr { AnyPtr::default() } -fn t11() -> Ptr:: { - Ptr::::null() -} - -fn t12() -> Ptr:: { - Ptr::::null() -} - -fn t13() -> Ptr:: { - Ptr::::null() -} - -fn t14() -> Ptr:: { - Ptr::::null() -} - fn t20() -> i8 { 0_i8 } -fn t21() -> Ptr:: { - Ptr::::null() -} - -fn t22() -> Ptr:: { - Ptr::::null() -} - -fn t23() -> Ptr:: { - Ptr::::null() -} - -fn t24() -> Ptr:: { - Ptr::::null() -} - -fn t31() -> Ptr:: { - Ptr::::null() -} - -fn t32() -> Ptr:: { - Ptr::::null() -} - -fn t33() -> Ptr:: { - Ptr::::null() -} - -fn t34() -> Ptr:: { - Ptr::::null() -} - -fn t41() -> Ptr:: { - Ptr::::null() -} - -fn t42() -> Ptr:: { - Ptr::::null() -} - -fn t43() -> Ptr:: { - Ptr::::null() -} - -fn t44() -> Ptr:: { - Ptr::::null() -} - -fn t51() -> Ptr:: { - Ptr::::null() -} - -fn t52() -> Ptr:: { - Ptr::::null() -} - -fn t53() -> Ptr:: { - Ptr::::null() -} - -fn t54() -> Ptr:: { - Ptr::::null() -} - -fn t61() -> Ptr:: { - Ptr::::null() -} - -fn t62() -> Ptr:: { - Ptr::::null() -} - -fn t63() -> Ptr:: { - Ptr::::null() -} - -fn t64() -> Ptr:: { - Ptr::::null() -} - -fn t71() -> Ptr:: { - Ptr::::null() -} - -fn t72() -> Ptr:: { - Ptr::::null() -} - -fn t73() -> Ptr:: { - Ptr::::null() -} - -fn t74() -> Ptr:: { - Ptr::::null() -} - -fn t81() -> Ptr:: { - Ptr::::null() -} - -fn t82() -> Ptr:: { - Ptr::::null() -} - -fn t83() -> Ptr:: { - Ptr::::null() -} - -fn t84() -> Ptr:: { - Ptr::::null() -} - -fn t91() -> Ptr:: { - Ptr::::null() -} - -fn t92() -> Ptr:: { - Ptr::::null() -} - -fn t93() -> Ptr:: { - Ptr::::null() -} - -fn t94() -> Ptr:: { - Ptr::::null() -} - -fn t101() -> Ptr:: { - Ptr::::null() -} - -fn t102() -> Ptr:: { - Ptr::::null() -} - -fn t103() -> Ptr:: { - Ptr::::null() -} - -fn t104() -> Ptr:: { - Ptr::::null() -} - -fn t111() -> Ptr:: { - Ptr::::null() -} - -fn t112() -> Ptr:: { - Ptr::::null() -} - -fn t113() -> Ptr:: { - Ptr::::null() -} - -fn t114() -> Ptr:: { - Ptr::::null() -} - -fn t121() -> Ptr:: { - Ptr::::null() -} - -fn t122() -> Ptr:: { - Ptr::::null() -} - -fn t123() -> Ptr:: { - Ptr::::null() -} - -fn t124() -> Ptr:: { - Ptr::::null() -} - -fn t131() -> Ptr:: { - Ptr::::null() -} - -fn t132() -> Ptr:: { - Ptr::::null() -} - -fn t133() -> Ptr:: { - Ptr::::null() -} - -fn t134() -> Ptr:: { - Ptr::::null() -} - -fn t141() -> Ptr:: { - Ptr::::null() -} - -fn t142() -> Ptr:: { - Ptr::::null() -} - -fn t143() -> Ptr:: { - Ptr::::null() -} - -fn t144() -> Ptr:: { - Ptr::::null() -} - -fn t151() -> Ptr:: { - Ptr::::null() -} - -fn t152() -> Ptr:: { - Ptr::::null() -} - -fn t153() -> Ptr:: { - Ptr::::null() -} - -fn t154() -> Ptr:: { - Ptr::::null() -} - -fn t161() -> Ptr:: { - Ptr::::null() -} - -fn t162() -> Ptr:: { - Ptr::::null() -} - -fn t163() -> Ptr:: { - Ptr::::null() -} - -fn t164() -> Ptr:: { - Ptr::::null() -} - -fn t171() -> Ptr:: { - Ptr::::null() -} - -fn t172() -> Ptr:: { - Ptr::::null() -} - -fn t173() -> Ptr:: { - Ptr::::null() -} - -fn t174() -> Ptr:: { - Ptr::::null() -} - -fn t181() -> Ptr:: { - Ptr::::null() -} - -fn t182() -> Ptr:: { - Ptr::::null() -} - -fn t183() -> Ptr:: { - Ptr::::null() -} - -fn t184() -> Ptr:: { - Ptr::::null() -} - -fn t191() -> Ptr:: { - Ptr::::null() -} - -fn t192() -> Ptr:: { - Ptr::::null() -} - -fn t193() -> Ptr:: { - Ptr::::null() -} - -fn t194() -> Ptr:: { - Ptr::::null() -} - -fn t201() -> Ptr:: { - Ptr::::null() -} - -fn t202() -> Ptr:: { - Ptr::::null() -} - -fn t203() -> Ptr:: { - Ptr::::null() -} - -fn t204() -> Ptr:: { - Ptr::::null() -} - -fn t211() -> Ptr:: { - Ptr::::null() -} - -fn t212() -> Ptr:: { - Ptr::::null() -} - -fn t213() -> Ptr:: { - Ptr::::null() -} - -fn t214() -> Ptr:: { - Ptr::::null() -} - fn t221() -> AnyPtr { AnyPtr::default() } diff --git a/rules/builtin_types/tgt_unsafe.rs b/rules/builtin_types/tgt_unsafe.rs index a31f3a375..b101d49e2 100644 --- a/rules/builtin_types/tgt_unsafe.rs +++ b/rules/builtin_types/tgt_unsafe.rs @@ -9,422 +9,86 @@ fn t10() -> bool { false } -fn t11() -> *mut bool { - std::ptr::null_mut() -} - -fn t12() -> *const bool { - std::ptr::null() -} - -fn t13() -> *mut bool { - std::ptr::null_mut() -} - -fn t14() -> *const bool { - std::ptr::null() -} - fn t20() -> libc::c_char { (0 as libc::c_char) } -fn t21() -> *mut libc::c_char { - std::ptr::null_mut() -} - -fn t22() -> *const libc::c_char { - std::ptr::null() -} - -fn t23() -> *mut libc::c_char { - std::ptr::null_mut() -} - -fn t24() -> *const libc::c_char { - std::ptr::null() -} - fn t30() -> i8 { 0_i8 } -fn t31() -> *mut i8 { - std::ptr::null_mut() -} - -fn t32() -> *const i8 { - std::ptr::null() -} - -fn t33() -> *mut i8 { - std::ptr::null_mut() -} - -fn t34() -> *const i8 { - std::ptr::null() -} - fn t40() -> u8 { 0_u8 } -fn t41() -> *mut u8 { - std::ptr::null_mut() -} - -fn t42() -> *const u8 { - std::ptr::null() -} - -fn t43() -> *mut u8 { - std::ptr::null_mut() -} - -fn t44() -> *const u8 { - std::ptr::null() -} - fn t50() -> u8 { 0_u8 } -fn t51() -> *mut u8 { - std::ptr::null_mut() -} - -fn t52() -> *const u8 { - std::ptr::null() -} - -fn t53() -> *mut u8 { - std::ptr::null_mut() -} - -fn t54() -> *const u8 { - std::ptr::null() -} - fn t60() -> i16 { 0_i16 } -fn t61() -> *mut i16 { - std::ptr::null_mut() -} - -fn t62() -> *const i16 { - std::ptr::null() -} - -fn t63() -> *mut i16 { - std::ptr::null_mut() -} - -fn t64() -> *const i16 { - std::ptr::null() -} - fn t70() -> u16 { 0_u16 } -fn t71() -> *mut u16 { - std::ptr::null_mut() -} - -fn t72() -> *const u16 { - std::ptr::null() -} - -fn t73() -> *mut u16 { - std::ptr::null_mut() -} - -fn t74() -> *const u16 { - std::ptr::null() -} - fn t80() -> u16 { 0_u16 } -fn t81() -> *mut u16 { - std::ptr::null_mut() -} - -fn t82() -> *const u16 { - std::ptr::null() -} - -fn t83() -> *mut u16 { - std::ptr::null_mut() -} - -fn t84() -> *const u16 { - std::ptr::null() -} - fn t90() -> i32 { 0_i32 } -fn t91() -> *mut i32 { - std::ptr::null_mut() -} - -fn t92() -> *const i32 { - std::ptr::null() -} - -fn t93() -> *mut i32 { - std::ptr::null_mut() -} - -fn t94() -> *const i32 { - std::ptr::null() -} - fn t100() -> u32 { 0_u32 } -fn t101() -> *mut u32 { - std::ptr::null_mut() -} - -fn t102() -> *const u32 { - std::ptr::null() -} - -fn t103() -> *mut u32 { - std::ptr::null_mut() -} - -fn t104() -> *const u32 { - std::ptr::null() -} - fn t110() -> i32 { 0_i32 } -fn t111() -> *mut i32 { - std::ptr::null_mut() -} - -fn t112() -> *const i32 { - std::ptr::null() -} - -fn t113() -> *mut i32 { - std::ptr::null_mut() -} - -fn t114() -> *const i32 { - std::ptr::null() -} - fn t120() -> u32 { 0_u32 } -fn t121() -> *mut u32 { - std::ptr::null_mut() -} - -fn t122() -> *const u32 { - std::ptr::null() -} - -fn t123() -> *mut u32 { - std::ptr::null_mut() -} - -fn t124() -> *const u32 { - std::ptr::null() -} - fn t130() -> f32 { 0_f32 } -fn t131() -> *mut f32 { - std::ptr::null_mut() -} - -fn t132() -> *const f32 { - std::ptr::null() -} - -fn t133() -> *mut f32 { - std::ptr::null_mut() -} - -fn t134() -> *const f32 { - std::ptr::null() -} - fn t140() -> i64 { 0_i64 } -fn t141() -> *mut i64 { - std::ptr::null_mut() -} - -fn t142() -> *const i64 { - std::ptr::null() -} - -fn t143() -> *mut i64 { - std::ptr::null_mut() -} - -fn t144() -> *const i64 { - std::ptr::null() -} - fn t150() -> u64 { 0_u64 } -fn t151() -> *mut u64 { - std::ptr::null_mut() -} - -fn t152() -> *const u64 { - std::ptr::null() -} - -fn t153() -> *mut u64 { - std::ptr::null_mut() -} - -fn t154() -> *const u64 { - std::ptr::null() -} - fn t160() -> i64 { 0_i64 } -fn t161() -> *mut i64 { - std::ptr::null_mut() -} - -fn t162() -> *const i64 { - std::ptr::null() -} - -fn t163() -> *mut i64 { - std::ptr::null_mut() -} - -fn t164() -> *const i64 { - std::ptr::null() -} - fn t170() -> u64 { 0_u64 } -fn t171() -> *mut u64 { - std::ptr::null_mut() -} - -fn t172() -> *const u64 { - std::ptr::null() -} - -fn t173() -> *mut u64 { - std::ptr::null_mut() -} - -fn t174() -> *const u64 { - std::ptr::null() -} - fn t180() -> f64 { 0_f64 } -fn t181() -> *mut f64 { - std::ptr::null_mut() -} - -fn t182() -> *const f64 { - std::ptr::null() -} - -fn t183() -> *mut f64 { - std::ptr::null_mut() -} - -fn t184() -> *const f64 { - std::ptr::null() -} - fn t190() -> f64 { 0_f64 } -fn t191() -> *mut f64 { - std::ptr::null_mut() -} - -fn t192() -> *const f64 { - std::ptr::null() -} - -fn t193() -> *mut f64 { - std::ptr::null_mut() -} - -fn t194() -> *const f64 { - std::ptr::null() -} - fn t200() -> i128 { 0_i128 } -fn t201() -> *mut i128 { - std::ptr::null_mut() -} - -fn t202() -> *const i128 { - std::ptr::null() -} - -fn t203() -> *mut i128 { - std::ptr::null_mut() -} - -fn t204() -> *const i128 { - std::ptr::null() -} - fn t210() -> u128 { 0_u128 } -fn t211() -> *mut u128 { - std::ptr::null_mut() -} - -fn t212() -> *const u128 { - std::ptr::null() -} - -fn t213() -> *mut u128 { - std::ptr::null_mut() -} - -fn t214() -> *const u128 { - std::ptr::null() -} - fn t220() -> () { () } diff --git a/rules/common/traits.h b/rules/common/traits.h new file mode 100644 index 000000000..6e50a69e8 --- /dev/null +++ b/rules/common/traits.h @@ -0,0 +1,58 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +template struct EnableIf {}; + +template struct EnableIf { + using type = T; +}; + +template struct IsSame { + static constexpr bool value = false; +}; + +template struct IsSame { + static constexpr bool value = true; +}; + +template struct RemoveConst { + using type = T; +}; + +template struct RemoveConst { + using type = T; +}; + +template struct FindAlloc { + using type = D; +}; + +template class C, + typename A, typename... Args> +struct FindAlloc, Args...> { + using type = A; +}; + +template +struct FindAlloc : FindAlloc {}; + +template struct AllocOf { + using type = D; +}; + +template