diff --git a/CMakeLists.txt b/CMakeLists.txt index cf4cf7a501..fb2cc02976 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,7 +215,6 @@ add_custom_command( ) file(GLOB rule_subdirs CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/rules/*) -set(cpp_rules_ir_outputs) set(rust_rules_ir_outputs) foreach(_rule_dir IN LISTS rule_subdirs) file(GLOB _cpp_srcs ${_rule_dir}/src.c ${_rule_dir}/src.cpp) @@ -224,25 +223,6 @@ foreach(_rule_dir IN LISTS rule_subdirs) 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() - 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} ${PROJECT_SOURCE_DIR}/cpp2rust/cpp_rule_preprocessor.cpp ${RUSTLS_HEADER} ${BROTLI_HEADERS} - VERBATIM - ) - list(APPEND cpp_rules_ir_outputs ${_cpp_out}) - set(_rust_srcs) set(_rust_out) if(EXISTS ${_rule_dir}/tgt_unsafe.rs) @@ -271,8 +251,6 @@ 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}) - add_custom_target("preprocess-rust-rules" ALL DEPENDS ${rust_rules_ir_outputs}) diff --git a/cpp2rust/CMakeLists.txt b/cpp2rust/CMakeLists.txt index 1b7825e778..f0389dcdf5 100644 --- a/cpp2rust/CMakeLists.txt +++ b/cpp2rust/CMakeLists.txt @@ -1,7 +1,6 @@ file(GLOB_RECURSE CORE_SOURCES "*.cpp") list(REMOVE_ITEM CORE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/cpp2rust.cpp" - "${CMAKE_CURRENT_SOURCE_DIR}/cpp_rule_preprocessor.cpp" ) add_library(cpp2rust_core STATIC ${CORE_SOURCES}) @@ -17,6 +16,7 @@ target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_C_COMPILER=\"${CMAKE_C_ target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_CXX_COMPILER=\"${CMAKE_CXX_COMPILER}\"") target_compile_definitions(cpp2rust_core PUBLIC "-DCLANG_RESOURCE_DIR=\"${LLVM_LIBRARY_DIR}/clang/${LLVM_VERSION_MAJOR}\"") target_compile_definitions(cpp2rust_core PUBLIC "-DCOMPAT_INCLUDE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/compat\"") +target_compile_definitions(cpp2rust_core PUBLIC "-DRULES_SOURCE_DIR=\"${PROJECT_SOURCE_DIR}/rules\"") target_compile_definitions(cpp2rust_core PUBLIC "-DRUST_STABLE_VERSION=\"${RUST_STABLE_VERSION}\"") if (APPLE) @@ -33,6 +33,3 @@ 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 cpp_rule_preprocessor.cpp) -target_link_libraries(cpp-rule-preprocessor PRIVATE cpp2rust_core) diff --git a/cpp2rust/ast_consumer.cpp b/cpp2rust/ast_consumer.cpp index b1611d83e9..86032b2751 100644 --- a/cpp2rust/ast_consumer.cpp +++ b/cpp2rust/ast_consumer.cpp @@ -4,10 +4,14 @@ #include "ast_consumer.h" #include "converter/converter.h" +#include "converter/rules/matcher.h" +#include "converter/rules/registry.h" +#include "converter/rules/rule_decls.h" namespace cpp2rust { void ASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { auto converter = CreateConverter(rs_code_, ctx, model_, rules_dir_); + RuleDecls::Collect(CI_.getSema()); converter->SetSema(CI_.getSema()); if (first_) { converter->EmitFilePreamble(); diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 8759208d4a..0d83ac1dbe 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -23,6 +23,7 @@ #include "converter/lex.h" #include "converter/mapper.h" #include "converter/printer.h" +#include "converter/rules/matcher.h" #include "converter/rules/registry.h" namespace cpp2rust { @@ -121,7 +122,7 @@ bool Converter::Convert(clang::QualType qual_type) { record_decls_.MarkReferenced(GetRecordName(decl)); } - auto mapped = Mapper::Map(ctx_, qual_type); + auto mapped = Mapper::Map(*this, qual_type); if (!mapped.empty() && mapped != token::kIgnoreRule) { StrCat(mapped); return false; @@ -131,8 +132,12 @@ 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(ctx_, qual_type); + std::string type_as_string = Mapper::Map(*this, qual_type); if (type_as_string == token::kIgnoreRule) { return false; } @@ -154,7 +159,7 @@ std::string Converter::ConvertPointeeType(clang::QualType ptr_type) { } bool Converter::VisitBuiltinType(clang::BuiltinType *type) { - auto mapped = Mapper::Map(ctx_, clang::QualType(type, 0)); + auto mapped = Mapper::Map(*this, clang::QualType(type, 0)); if (mapped.empty()) { llvm::report_fatal_error(llvm::Twine("no type rule for builtin type: ") + type->getName(ctx_.getPrintingPolicy())); @@ -181,7 +186,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; } @@ -312,7 +321,7 @@ bool Converter::VisitPointerType(clang::PointerType *type) { auto pointee_type = type->getPointeeType(); StrCat(pointee_type.isConstQualified() ? "*const" : "*mut"); if (pointee_type->isRecordType() && - abstract_structs_.contains(GetID(pointee_type->getAsRecordDecl()))) { + IsAbstractUserClass(pointee_type->getAsRecordDecl())) { StrCat(keyword::kDyn); } return Convert(pointee_type); @@ -681,17 +690,25 @@ bool Converter::TypeDerivesDefault(clang::QualType qual_type) { } bool Converter::IsPassThroughRule(clang::Expr *expr) const { - const auto *rule = RuleRegistry::GetExprRule(ctx_, GetCalleeOrExpr(expr)); + const auto *rule = RuleRegistry::GetExprRule(ctx_, expr); return rule && rule->body.size() == 1 && std::holds_alternative( 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) { @@ -702,7 +719,7 @@ bool Converter::RecordHasCopyableFields(const clang::RecordDecl *decl) { for (auto f : decl->fields()) { // Records that contain std::vector, std::array, std::string or anything // that is translated to Vec<>, do not derive Copy - auto mapped = Mapper::Map(ctx_, f->getType()); + auto mapped = Mapper::Map(*this, f->getType()); if (mapped.starts_with("Vec<")) { return false; } @@ -750,7 +767,6 @@ bool Converter::VisitRecordDecl(clang::RecordDecl *decl) { return false; } - RuleRegistry::AddRuleForUserDefinedType(ctx_, decl); EmitRustStructOrUnion(decl); return false; @@ -817,9 +833,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, ','); @@ -917,9 +930,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, ','); @@ -943,7 +953,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; } @@ -1491,7 +1500,7 @@ bool Converter::VisitCXXForRangeStmtMap(clang::CXXForRangeStmt *stmt) { auto loop_var_name = GetNamedDeclAsString(loop_var); StrCat("'loop_:"); - auto map_type = Mapper::Map(ctx_, stmt->getRangeInit()->getType()); + auto map_type = Mapper::Map(*this, stmt->getRangeInit()->getType()); StrCat(keyword::kFor, loop_var_name, keyword::kIn, "UnsafeMapIterator::begin(&"); Convert(stmt->getRangeInit()); @@ -1657,10 +1666,10 @@ bool Converter::GetFmtArg(clang::Expr *arg, std::string &fmt, fmt_trait = "x"; } else if (arg_str.contains("std::dec")) { fmt_trait = ""; - } else if (arg_str.contains("Setw")) { + } else if (IsStdSetw(arg)) { fmt_width = Trim(ToString(arg)); } else if (!arg->getType()->isCharType() && - Mapper::Map(ctx_, arg->getType()) != + Mapper::Map(*this, arg->getType()) != std::format("Vec<{}>", CharRustType())) { fmt += ("{:" + fmt_width + fmt_trait + "}"); fmt_width.clear(); // Reset setw after first usage @@ -1678,7 +1687,7 @@ bool Converter::GetFmtArg(clang::Expr *arg, std::string &fmt, bool Converter::GetRawArg(clang::Expr *arg, std::string &raw_args) { if (arg->getType()->isCharType()) { raw_args += "(&[" + ToString(arg) + " as u8]"; - } else if (Mapper::Map(ctx_, arg->getType()) == + } else if (Mapper::Map(*this, arg->getType()) == std::format("Vec<{}>", CharRustType())) { PushExprKind push(*this, ExprKind::RValue); std::string str = ToString(arg); @@ -1834,15 +1843,14 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) { return false; } - if (IsImplicitAssignmentCall(expr) && - !Mapper::Contains(ctx_, expr->getCallee())) { + if (IsImplicitAssignmentCall(expr) && !Mapper::Contains(ctx_, expr)) { auto *call = clang::cast(expr); ConvertAssignment(call->getImplicitObjectArgument(), call->getArg(0), "="); return false; } - if (Mapper::Contains(ctx_, expr->getCallee())) { - if (RuleRegistry::IsLibcPassthrough(ctx_, GetCalleeOrExpr(expr))) { + if (Mapper::Contains(ctx_, expr)) { + if (RuleRegistry::IsLibcPassthrough(ctx_, expr)) { ConvertGenericCallExpr(expr); return false; } @@ -1882,8 +1890,7 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) { } if (auto *opcall = clang::dyn_cast(expr); - opcall && !IsUserOperatorCall(opcall) && - !Mapper::Contains(ctx_, expr->getCallee())) { + opcall && !IsUserOperatorCall(opcall) && !Mapper::Contains(ctx_, expr)) { return ConvertCXXOperatorCallExpr(opcall); } @@ -1981,8 +1988,7 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { function ? function->getNumParams() : proto->getNumParams(); info.is_variadic = function ? function->isVariadic() : proto->isVariadic(); info.is_fn_ptr_call = !function; - info.is_libc_passthrough = - RuleRegistry::IsLibcPassthrough(ctx_, GetCalleeOrExpr(expr)); + info.is_libc_passthrough = RuleRegistry::IsLibcPassthrough(ctx_, expr); for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { auto *arg = expr->getArg(i + arg_begin); @@ -2130,9 +2136,8 @@ void Converter::EmitArgList(const CallInfo &info) { case Kind::Inline: ConvertParamTy(ca.param_type, ca.expr); if (info.is_libc_passthrough) { - StrCat(std::format( - "as {}", - Mapper::GetParamType(ctx_, GetCalleeOrExpr(info.expr), i))); + StrCat( + std::format("as {}", Mapper::GetParamType(*this, info.expr, i))); } break; } @@ -2224,7 +2229,7 @@ Converter::ConvertCallExpr(clang::CallExpr *expr) { ) ? token::kOne : token::kZero); - } else if (Mapper::Contains(ctx_, callee)) { + } else if (Mapper::Contains(ctx_, expr)) { auto **args = expr->getArgs(); auto num_args = expr->getNumArgs(); auto ctx = CollectRefBindingTempArgs(expr); @@ -2268,7 +2273,7 @@ std::string Converter::getIntegerLiteral(clang::IntegerLiteral *expr, if (ty->isFloatingType() || incl_type) { if (value.isZero()) { - if (auto init = Mapper::MapInitializer(ctx_, ty); !init.empty()) { + if (auto init = Mapper::MapInitializer(*this, ty); !init.empty()) { return init; } } @@ -2284,7 +2289,7 @@ bool Converter::VisitIntegerLiteral(clang::IntegerLiteral *expr) { computed_expr_type_ = ComputedExprType::FreshValue; return false; } - StrCat(getIntegerLiteral(expr, Mapper::Map(ctx_, expr->getType()) != "i32")); + StrCat(getIntegerLiteral(expr, Mapper::Map(*this, expr->getType()) != "i32")); computed_expr_type_ = ComputedExprType::FreshValue; return false; } @@ -3790,7 +3795,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()))); @@ -4052,7 +4056,7 @@ std::string Converter::GetDefaultAsString(clang::QualType qual_type) { return arr; } - if (auto init = Mapper::MapInitializer(ctx_, qual_type); !init.empty()) { + if (auto init = Mapper::MapInitializer(*this, qual_type); !init.empty()) { computed_expr_type_ = ComputedExprType::FreshValue; return init; } @@ -4245,7 +4249,7 @@ void Converter::ConvertUnsignedArithOperand(clang::Expr *expr, bool needs_cast = (expr->isIntegerConstantExpr(ctx_) && !clang::isa(expr)) || - Mapper::Map(ctx_, expr->getType()) != Mapper::Map(ctx_, type); + Mapper::Map(*this, expr->getType()) != Mapper::Map(*this, type); PushParen paren(*this, needs_cast); Convert(expr); if (needs_cast) { @@ -4345,7 +4349,7 @@ void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx, Convert(idx); } - if (Mapper::Map(ctx_, idx->getType()) != "usize") { + if (Mapper::Map(*this, idx->getType()) != "usize") { StrCat(keyword::kAs, "usize"); } } @@ -4889,9 +4893,8 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, } if (ph_ctx.declared_in_rule_as_rust_ptr && arg->getType()->isArrayType()) { - return std::format( - "({} as {})", ConvertFreshPointer(arg), - Mapper::GetParamType(ctx_, GetCalleeOrExpr(expr), ph_ctx.arg_idx)); + return std::format("({} as {})", ConvertFreshPointer(arg), + Mapper::GetParamType(*this, expr, ph_ctx.arg_idx)); } if (ph_ctx.needs_materialization()) { @@ -4906,8 +4909,7 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, } if (ph_ctx.needs_pointer_receiver()) { - auto param_type = - Mapper::GetParamType(ctx_, GetCalleeOrExpr(expr), ph_ctx.arg_idx); + auto param_type = Mapper::GetParamType(*this, expr, ph_ctx.arg_idx); return std::format("({} as {})", ConvertFreshObject(arg, param_type), param_type); } @@ -4973,7 +4975,7 @@ std::string Converter::ConvertMappedMethodCall( std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx) { - auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, GetCalleeOrExpr(expr)); + auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, expr); if (!tgt_ir) return {}; @@ -4997,7 +4999,7 @@ std::string Converter::ConvertIRFragment( if (auto *t = std::get_if(&frag)) { result += t->text; } else if (auto *g = std::get_if(&frag)) { - result += Mapper::InstantiateTemplate(ctx_, GetCalleeOrExpr(expr), g->n); + result += Mapper::InstantiateTemplate(*this, expr, g->n); } else if (auto *ph = std::get_if(&frag)) { auto arg_idx = ph->n; assert(arg_idx < all_args.size()); @@ -5014,8 +5016,8 @@ std::string Converter::ConvertIRFragment( .is_receiver = is_receiver, .is_cpp_ptr = arg->getType()->isPointerType(), .maps_to_rust_ptr = RuleRegistry::MapsToPointer(ctx_, arg->getType()), - .declared_in_rule_as_rust_ptr = RuleRegistry::ParamIsPointer( - ctx_, GetCalleeOrExpr(expr), arg_idx), + .declared_in_rule_as_rust_ptr = + RuleRegistry::ParamIsPointer(ctx_, expr, arg_idx), .is_index_base = ph->is_index_base, }; result += ConvertPlaceholder(expr, arg, ph_ctx); @@ -5035,7 +5037,7 @@ std::string Converter::ConvertIRFragment( std::string Converter::ConvertVariadicTail(clang::Expr *expr, const std::vector &all_args) { - const auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, GetCalleeOrExpr(expr)); + const auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, expr); unsigned fixed = tgt_ir ? tgt_ir->params.size() : 0; Buffer buf(*this); @@ -5054,14 +5056,9 @@ Converter::ConvertVariadicTail(clang::Expr *expr, std::string Converter::ConvertInitFragment(clang::Expr *expr, const std::vector &all_args) { - const auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, GetCalleeOrExpr(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(); + const auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, expr); + assert(tgt_ir && tgt_ir->usesInit()); + auto type = Matcher::GetInitType(ctx_, expr); Buffer buf(*this); ConvertConstructFromArgs( diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 439eb7ace5..7f84b94400 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -48,6 +48,10 @@ class Converter : public clang::RecursiveASTVisitor { return *sema_; } + clang::ASTContext &GetASTContext() { return ctx_; } + + virtual std::string GetUnboxedTypeAsString(clang::QualType qual_type); + bool VisitRecoveryExpr(clang::RecoveryExpr *expr); virtual void EmitFilePreamble(); @@ -61,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); @@ -345,7 +351,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(); } @@ -736,7 +742,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 c7f251517a..fdf2edec3c 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -231,6 +231,13 @@ bool IsUserDefinedDecl(const clang::Decl *decl) { !src_mgr.isInSystemMacro(src_loc); } +bool IsAbstractUserClass(const clang::RecordDecl *decl) { + auto *cxx = llvm::dyn_cast(decl); + auto *definition = cxx ? cxx->getDefinition() : nullptr; + return definition && IsUserDefinedDecl(definition) && + definition->isAbstract(); +} + bool RefersToUserDefinedDecl(const clang::Expr *expr) { expr = expr->IgnoreParenImpCasts(); const clang::Decl *decl = nullptr; @@ -1248,18 +1255,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)) { @@ -1458,11 +1453,16 @@ clang::Expr *GetCallee(clang::CallExpr *expr) { return expr->getCallee(); } -clang::Expr *GetCalleeOrExpr(clang::Expr *expr) { - if (auto *call = clang::dyn_cast(expr)) { - return call->getCallee(); +bool IsStdSetw(const clang::Expr *expr) { + expr = expr->IgnoreImplicit(); + if (const auto *construct = llvm::dyn_cast(expr); + construct && construct->isElidable()) { + expr = construct->getArg(0)->IgnoreImplicit(); } - return expr; + const auto *call = llvm::dyn_cast(expr); + const auto *callee = call ? call->getDirectCallee() : nullptr; + return callee && callee->isInStdNamespace() && callee->getIdentifier() && + callee->getName() == "setw"; } bool HasReceiver(clang::Expr *expr) { @@ -1497,13 +1497,10 @@ std::optional GetParamImplicitConvertTarget(clang::Expr *expr, std::optional GetStrongestIteratorCategory(clang::ASTContext &ctx, clang::QualType type) { type = type.getNonReferenceType().getUnqualifiedType(); - if (!Mapper::Contains(ctx, type)) { - return std::nullopt; - } if (RuleRegistry::MapsToRefcountPointer(ctx, type)) { return IteratorCategory::Contiguous; } - auto mapped = Mapper::Map(ctx, type); + auto mapped = Mapper::MapUninstantiated(ctx, type); if (mapped.empty()) { return std::nullopt; } @@ -1586,7 +1583,8 @@ bool NeedsImplicitScalarCast(clang::ASTContext &ctx, clang::QualType from, to->isIntegerType() && from.getCanonicalType().getUnqualifiedType() == to.getCanonicalType().getUnqualifiedType() && - Mapper::Map(ctx, from) != Mapper::Map(ctx, to); + Mapper::MapUninstantiated(ctx, from) != + Mapper::MapUninstantiated(ctx, to); } clang::QualType GetExprPointee(clang::ASTContext &ctx, const clang::Expr *from, @@ -1640,7 +1638,8 @@ static bool PointeeMappingDiffers(clang::ASTContext &ctx, clang::QualType from, } return from.getCanonicalType().getUnqualifiedType() == to.getCanonicalType().getUnqualifiedType() && - Mapper::Map(ctx, from) != Mapper::Map(ctx, to); + Mapper::MapUninstantiated(ctx, from) != + Mapper::MapUninstantiated(ctx, to); } bool NeedsImplicitPointeeCast(clang::ASTContext &ctx, const clang::Expr *from, @@ -1679,7 +1678,7 @@ bool NeedsRefBindingTemp(clang::ASTContext &ctx, const clang::Expr *arg, } bool IsSizeType(clang::ASTContext &ctx, clang::QualType type) { - auto rust_type = Mapper::Map(ctx, type); + auto rust_type = Mapper::MapUninstantiated(ctx, type); return rust_type == "usize" || rust_type == "isize"; } diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 53239f83b3..c22c08a308 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -62,6 +62,8 @@ bool IsCodeUnitStringLiteral(const clang::StringLiteral *expr); bool IsUserDefinedDecl(const clang::Decl *decl); +bool IsAbstractUserClass(const clang::RecordDecl *decl); + bool RefersToUserDefinedDecl(const clang::Expr *expr); bool IsUnsignedArithOp(const clang::BinaryOperator *expr); @@ -217,9 +219,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); @@ -255,7 +254,7 @@ bool MayCauseBorrowMutError(const clang::Expr *lhs, const clang::Expr *rhs); bool ArgsMayAlias(const clang::Expr *a, const clang::Expr *b); -clang::Expr *GetCalleeOrExpr(clang::Expr *expr); +bool IsStdSetw(const clang::Expr *expr); bool HasReceiver(clang::Expr *expr); diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 1fe6c4c0d0..add47a3b2d 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -11,6 +11,7 @@ #include #include +#include "converter/converter.h" #include "converter/converter_lib.h" #include "converter/rules/matcher.h" #include "converter/rules/registry.h" @@ -29,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) && Matcher::HasRuleNamed(decl)) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), RuleRegistry::CurrentModel() == Model::kRefCount ? "refcount" @@ -38,43 +39,44 @@ std::string MapFunctionName(clang::ASTContext &ctx, return GetNamedDeclAsString(decl->getCanonicalDecl()); } -std::string InstantiateTemplate(clang::ASTContext &ctx, const clang::Expr *expr, +std::string InstantiateTemplate(Converter &converter, const clang::Expr *expr, unsigned n) { - auto [rule, subs] = RuleRegistry::Search(ctx, expr); + auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), expr); auto text = std::format("T{}", n); if (!rule) { return text; } - 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); +std::string Map(Converter &converter, clang::QualType qual_type) { + auto [rule, subs] = + RuleRegistry::Search(converter.GetASTContext(), qual_type); if (rule) { - return Matcher::InstantiateTgt(Matcher::MapBindings(subs), - rule->type_info.type); + return Matcher::InstantiateTgt(converter, subs, rule->type_info.type); } return {}; } -std::string MapInitializer(clang::ASTContext &ctx, clang::QualType qual_type) { - auto [rule, subs] = RuleRegistry::Search(ctx, qual_type); +std::string MapUninstantiated(clang::ASTContext &ctx, + clang::QualType qual_type) { + auto rule = RuleRegistry::Search(ctx, qual_type).first; + return rule ? rule->type_info.type : std::string(); +} + +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 {}; } -std::string GetParamType(clang::ASTContext &ctx, const clang::Expr *expr, +std::string GetParamType(Converter &converter, const clang::Expr *expr, unsigned index) { - auto [rule, subs] = RuleRegistry::Search(ctx, expr); - return Matcher::InstantiateTgt(Matcher::MapBindings(subs), - rule->params.at(index).type); + auto [rule, subs] = RuleRegistry::Search(converter.GetASTContext(), expr); + return Matcher::InstantiateTgt(converter, subs, rule->params.at(index).type); } } // namespace cpp2rust::Mapper diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index 8d4838492d..6a2dbce4c6 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -11,16 +11,22 @@ #include "converter/translation_rule.h" +namespace cpp2rust { +class Converter; +} + namespace cpp2rust::Mapper { bool Contains(clang::ASTContext &ctx, clang::QualType qual_type); bool Contains(clang::ASTContext &ctx, const clang::Expr *expr); -std::string Map(clang::ASTContext &ctx, clang::QualType qual_type); -std::string MapInitializer(clang::ASTContext &ctx, clang::QualType qual_type); +std::string Map(Converter &converter, clang::QualType qual_type); +std::string MapUninstantiated(clang::ASTContext &ctx, + clang::QualType qual_type); +std::string MapInitializer(Converter &converter, clang::QualType qual_type); std::string MapFunctionName(clang::ASTContext &ctx, const clang::FunctionDecl *decl); -std::string InstantiateTemplate(clang::ASTContext &ctx, const clang::Expr *expr, +std::string InstantiateTemplate(Converter &converter, const clang::Expr *expr, unsigned n); -std::string GetParamType(clang::ASTContext &ctx, const clang::Expr *expr, +std::string GetParamType(Converter &converter, const clang::Expr *expr, unsigned index); } // namespace cpp2rust::Mapper diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index b6865e10c3..c1a5710d24 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -52,7 +52,7 @@ static bool IsBoxedType(std::string_view type) { } static bool IsBoxedType(clang::ASTContext &ctx, clang::QualType type) { - return IsBoxedType(Mapper::Map(ctx, type.getUnqualifiedType())); + return IsBoxedType(Mapper::MapUninstantiated(ctx, type.getUnqualifiedType())); } // Virtual methods take &mut self, as the fields of the struct are stored in @@ -196,6 +196,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)) { @@ -276,7 +282,7 @@ bool ConverterRefCount::VisitPointerType(clang::PointerType *type) { PushConversionKind push2(*this, ConversionKind::FullRefCount, pointee_type->isArrayType()); if (pointee_type->isRecordType() && - abstract_structs_.contains(GetID(pointee_type->getAsRecordDecl()))) { + IsAbstractUserClass(pointee_type->getAsRecordDecl())) { StrCat("PtrDyn(attrs.begin(), attrs.end())); auto size = ctx_.getTypeSizeInChars(ctx_.getCanonicalTagType(decl)); StrCat(std::format( @@ -1162,8 +1165,7 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) { return false; } - if (IsImplicitAssignmentCall(expr) && - !Mapper::Contains(ctx_, expr->getCallee())) { + if (IsImplicitAssignmentCall(expr) && !Mapper::Contains(ctx_, expr)) { auto *call = clang::cast(expr); ConvertAssignment(call->getImplicitObjectArgument(), call->getArg(0), "="); return false; @@ -1174,8 +1176,7 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) { } if (auto *opcall = clang::dyn_cast(expr); - opcall && !IsUserOperatorCall(opcall) && - !Mapper::Contains(ctx_, expr->getCallee())) { + opcall && !IsUserOperatorCall(opcall) && !Mapper::Contains(ctx_, expr)) { return ConvertCXXOperatorCallExpr(opcall); } @@ -2016,7 +2017,7 @@ bool ConverterRefCount::VisitCXXForRangeStmtMap(clang::CXXForRangeStmt *stmt) { EmitByValueShadow( loop_var_name, loop_var->getType(), std::string(loop_var_name), - "Value<" + Mapper::Map(ctx_, GetForRangeIteratorType(stmt)) + '>'); + "Value<" + Mapper::Map(*this, GetForRangeIteratorType(stmt)) + '>'); ConvertForRangeBody(stmt, loop_var); @@ -2246,7 +2247,7 @@ std::string ConverterRefCount::GetDefaultAsString(clang::QualType qual_type) { return BoxValue(std::move(arr)); } - if (auto init = Mapper::MapInitializer(ctx_, qual_type); !init.empty()) { + if (auto init = Mapper::MapInitializer(*this, qual_type); !init.empty()) { computed_expr_type_ = ComputedExprType::FreshValue; return BoxValue(std::move(init)); } @@ -2962,7 +2963,7 @@ std::string ConverterRefCount::ConvertMappedMethodCall( arg = call->getArg(0); } - auto param_type = Mapper::GetParamType(ctx_, GetCalleeOrExpr(expr), arg_idx); + auto param_type = Mapper::GetParamType(*this, expr, arg_idx); if (arg->getType()->isPointerType()) { return std::format("{}.with_mut(|__v: {}| __v{})", ConvertPointer(arg), diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 58cb4673a7..6a5000a0e3 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -12,6 +12,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/printer.cpp b/cpp2rust/converter/printer.cpp index 38e39c282c..481993fa57 100644 --- a/cpp2rust/converter/printer.cpp +++ b/cpp2rust/converter/printer.cpp @@ -128,6 +128,10 @@ std::string ToString(clang::ASTContext &ctx, clang::QualType qual_type, clang::dyn_cast(t.getTypePtr())) { t = decltype_type->getUnderlyingType(); } + if (const auto *typeof_type = + clang::dyn_cast(t.getTypePtr())) { + t = typeof_type->getUnderlyingExpr()->getType(); + } if (const auto *typedef_type = t->getAs()) { if (t.getCanonicalType()->isBuiltinType()) { return typedef_type->getDecl()->getNameAsString(); @@ -147,7 +151,8 @@ std::string ToString(clang::ASTContext &ctx, clang::QualType qual_type, ToString(ctx, pointee.getUnqualifiedType(), ScalarSugar::kPreserve)); } - if (Mapper::Map(ctx, pointee) == Mapper::Map(ctx, canonical)) { + if (Mapper::MapUninstantiated(ctx, pointee) == + Mapper::MapUninstantiated(ctx, canonical)) { pointee = canonical; } std::string out; diff --git a/cpp2rust/converter/rules/matcher.cpp b/cpp2rust/converter/rules/matcher.cpp index 1dd53121f1..4bf0450e5e 100644 --- a/cpp2rust/converter/rules/matcher.cpp +++ b/cpp2rust/converter/rules/matcher.cpp @@ -3,30 +3,609 @@ #include "converter/rules/matcher.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include +#include + +#include "converter/converter.h" +#include "converter/converter_lib.h" +#include "converter/mapper.h" +#include "converter/printer.h" +#include "converter/rules/registry.h" +#include "converter/rules/rule_decls.h" +#include "logging.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); +namespace { + +using ExprMatch = Match; +using TypeMatch = Match; + +struct Target { + const clang::Decl *decl = nullptr; + std::string macro; + int unary = -1; + int spelled = -1; + + bool operator==(const Target &) const = default; + explicit operator bool() const { return decl || !macro.empty(); } +}; + +Target getTarget(clang::ASTContext &ctx, const clang::Expr *expr) { + expr = RuleDecls::SkipImplicit(expr); + + if (llvm::isa(expr) && + expr->getBeginLoc().isMacroID()) { + Target target; + target.macro = clang::Lexer::getImmediateMacroName(expr->getBeginLoc(), + ctx.getSourceManager(), + ctx.getLangOpts()) + .str(); + return target; + } + if (const auto *rewritten = + llvm::dyn_cast(expr)) { + auto target = getTarget(ctx, rewritten->getSemanticForm()); + target.spelled = rewritten->getOperator(); + return target; + } + if (const auto *unary = llvm::dyn_cast(expr)) { + auto target = getTarget(ctx, unary->getSubExpr()); + target.unary = unary->getOpcode(); + return target; + } + Target target; + if (const auto *call = llvm::dyn_cast(expr)) { + if (const auto *callee = call->getDirectCallee()) { + target.decl = callee->getCanonicalDecl(); + } + } else if (const auto *construct = + llvm::dyn_cast(expr)) { + target.decl = construct->getConstructor()->getCanonicalDecl(); + } else if (const auto *member = llvm::dyn_cast(expr)) { + target.decl = member->getMemberDecl()->getCanonicalDecl(); + } else if (const auto *ref = llvm::dyn_cast(expr)) { + target.decl = ref->getDecl()->getCanonicalDecl(); + } else if (const auto *cast = llvm::dyn_cast(expr)) { + return getTarget(ctx, cast->getSubExpr()); + } + return target; +} + +clang::Expr *getObject(clang::ASTContext &ctx, clang::Expr *object) { + auto type = object->getType(); + if (type->isPointerType()) { + type = type->getPointeeType(); + } else if (!type.isConstQualified()) { + return object; + } + return new (ctx) clang::OpaqueValueExpr( + object->getBeginLoc(), type.getUnqualifiedType(), clang::VK_LValue); +} + +std::vector getOperands(clang::ASTContext &ctx, + const clang::Expr *use) { + auto *expr = const_cast(use); + 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)) { + if (const auto *method = llvm::dyn_cast_or_null( + call->getDirectCallee()); + method && method->isStatic()) { + operands.push_back(new (ctx) clang::OpaqueValueExpr( + call->getBeginLoc(), ctx.getCanonicalTagType(method->getParent()), + clang::VK_LValue)); + } + 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); + } else if (auto for_range = GetParentForRange(ctx, member); + for_range && !member->isArrow() && + GetClassName(for_range->getRangeInit()->getType()) == + "std::map") { + if (auto type = GetForRangeIteratorType(for_range); !type.isNull()) { + base = new (ctx) clang::OpaqueValueExpr(member->getBeginLoc(), type, + clang::VK_LValue); + } + } + operands.push_back(getObject(ctx, base)); + } + while (!operands.empty() && + llvm::isa(operands.back())) { + operands.pop_back(); + } + return operands; +} + +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_; +}; + +clang::FunctionDecl *deduce(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + clang::TemplateArgumentListInfo *explicit_args, + llvm::ArrayRef operands, + clang::SourceLocation loc, + clang::TemplateDeductionResult &result) { + clang::FunctionDecl *specialization = nullptr; + clang::sema::TemplateDeductionInfo info(loc); + auto check = [](llvm::ArrayRef, bool) { return false; }; + result = sema.DeduceTemplateArguments( + rule, explicit_args, operands, specialization, info, false, false, false, + clang::QualType(), clang::Expr::Classification(), false, check); + return result == clang::TemplateDeductionResult::Success ? specialization + : nullptr; +} + +clang::FunctionDecl * +deduceWithLeading(clang::Sema &sema, clang::FunctionTemplateDecl *rule, + llvm::ArrayRef leading, + llvm::ArrayRef operands, + clang::SourceLocation loc) { + clang::TemplateArgumentListInfo explicit_args(loc, loc); + for (const auto &arg : leading) { + if (arg.isNull()) { + break; + } + auto type = arg.getKind() == clang::TemplateArgument::Integral + ? arg.getIntegralType() + : clang::QualType(); + explicit_args.addArgument( + sema.getTrivialTemplateArgumentLoc(arg, type, loc)); + } + if (explicit_args.size() == 0) { + return nullptr; + } + clang::TemplateDeductionResult result; + return deduce(sema, rule, &explicit_args, operands, loc, result); +} + +std::vector +getArgsFromCallee(clang::FunctionTemplateDecl *rule, const Target &want) { + std::vector args( + rule->getTemplateParameters()->size()); + const auto *callee = llvm::dyn_cast_or_null(want.decl); + const auto *callee_args = + callee ? callee->getTemplateSpecializationArgs() : nullptr; + const auto *returned = RuleDecls::GetReturned(rule->getTemplatedDecl()); + const auto *call = + llvm::dyn_cast(RuleDecls::SkipImplicit(returned)); + const auto *written = call ? llvm::dyn_cast( + call->getCallee()->IgnoreParenImpCasts()) + : nullptr; + if (!callee_args || !written || !written->hasExplicitTemplateArgs()) { + return args; + } + unsigned position = 0; + for (const auto &arg : written->template_arguments()) { + if (position >= callee_args->size()) { + break; + } + int index = -1; + const auto &value = arg.getArgument(); + if (value.getKind() == clang::TemplateArgument::Type) { + if (const auto *param = + value.getAsType()->getAs()) { + index = param->getIndex(); + } + } else if (value.getKind() == clang::TemplateArgument::Expression) { + if (const auto *ref = llvm::dyn_cast( + value.getAsExpr()->IgnoreParenImpCasts())) { + if (const auto *param = llvm::dyn_cast( + ref->getDecl())) { + index = param->getIndex(); + } + } + } + if (index >= 0 && (unsigned)index < args.size()) { + args[index] = callee_args->get(position); + } + ++position; + } + return args; +} + +std::vector +getArgsFromResultType(clang::Sema &sema, clang::FunctionTemplateDecl *rule, + const clang::Expr *use) { + auto *params = rule->getTemplateParameters(); + llvm::SmallVector deduced(params->size()); + clang::sema::TemplateDeductionInfo info(use->getBeginLoc()); + clang::TemplateArgument pattern(rule->getTemplatedDecl()->getReturnType()); + clang::TemplateArgument actual(use->getType().getCanonicalType()); + if (sema.DeduceTemplateArguments(params, pattern, actual, info, deduced, + true) != + clang::TemplateDeductionResult::Success) { + return {}; + } + return {deduced.begin(), deduced.end()}; +} + +clang::Expr *substituteReturned(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + clang::FunctionDecl *specialization, + clang::SourceLocation loc) { + auto *pattern = rule->getTemplatedDecl(); + const auto *returned = RuleDecls::GetReturned(pattern); + clang::EnterExpressionEvaluationContext unevaluated( + sema, clang::Sema::ExpressionEvaluationContext::Unevaluated); + clang::Sema::InstantiatingTemplate instantiating(sema, loc, 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(returned), + sema.getTemplateInstantiationArgs(specialization)); + return result.isInvalid() ? nullptr : result.get(); +} + +bool resolvesTo(clang::Sema &sema, clang::FunctionTemplateDecl *rule, + clang::FunctionDecl *specialization, const Target &want, + clang::SourceLocation loc) { + if (!specialization) { + return false; + } + Trial trial(sema); + auto *returned = substituteReturned(sema, rule, specialization, loc); + return returned && !trial.failed() && + getTarget(sema.Context, returned) == want; +} + +clang::FunctionDecl *matchTemplate(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + const clang::Expr *use, const Target &want, + llvm::ArrayRef operands) { + auto loc = use->getBeginLoc(); + clang::FunctionDecl *specialization = nullptr; + { + Trial trial(sema); + clang::TemplateDeductionResult result; + specialization = deduce(sema, rule, nullptr, operands, loc, result); + } + if (resolvesTo(sema, rule, specialization, want, loc)) { + return specialization; + } + { + Trial trial(sema); + specialization = deduceWithLeading( + sema, rule, getArgsFromCallee(rule, want), operands, loc); + } + if (resolvesTo(sema, rule, specialization, want, loc)) { + return specialization; + } + { + Trial trial(sema); + specialization = deduceWithLeading( + sema, rule, getArgsFromResultType(sema, rule, use), operands, loc); + } + if (resolvesTo(sema, rule, specialization, want, loc)) { + return specialization; + } + return nullptr; +} + +unsigned getGenericNumber(const clang::NamedDecl *param) { + auto name = param->getName(); + unsigned n = 0; + if (!name.consume_front("T") || name.getAsInteger(10, n)) { + return 0; + } + return n; +} + +Bindings getBindings(const clang::TemplateParameterList *params, + llvm::ArrayRef args) { + Bindings bindings; + for (unsigned i = 0; i < params->size() && i < args.size(); ++i) { + auto n = getGenericNumber(params->getParam(i)); + if (!n || args[i].getKind() != clang::TemplateArgument::Type) { + continue; + } + if (bindings.size() < n) { + bindings.resize(n); + } + bindings[n - 1] = args[i].getAsType(); + } + return bindings; +} + +struct Candidate { + TranslationRule::ExprRule *rule = nullptr; + clang::QualType init_type; + clang::FunctionTemplateDecl *tmpl = nullptr; + clang::FunctionDecl *specialization = nullptr; +}; + +bool isBetter(clang::Sema &sema, const Candidate &a, const Candidate &b, + clang::SourceLocation loc, unsigned num_operands) { + if (!a.tmpl || !b.tmpl) { + return !a.tmpl && b.tmpl; + } + Trial trial(sema); + if (auto *better = sema.getMoreSpecializedTemplate( + a.tmpl, b.tmpl, loc, clang::TPOC_Call, num_operands)) { + return better == a.tmpl; + } + return a.tmpl->getTemplateParameters()->size() < + b.tmpl->getTemplateParameters()->size(); +} + +ExprMatch findExpr(clang::ASTContext &ctx, const clang::Expr *expr, + clang::QualType *init_type) { + auto &sema = RuleDecls::GetSema(); + const auto *use = expr; + auto want = getTarget(ctx, use); + if (!want) { + return {}; + } + auto operands = getOperands(ctx, use); + auto key = RuleDecls::GetExprKey(ctx, use); + + Candidate best; + auto consider = [&](const std::string &bucket) { + for (auto &[_, entry] : RuleDecls::ExprCandidates(bucket)) { + Candidate candidate{entry.rule, entry.init_type}; + auto *function = const_cast(entry.decl); + if (auto *tmpl = + function ? function->getDescribedFunctionTemplate() : nullptr) { + candidate.tmpl = tmpl; + candidate.specialization = + matchTemplate(sema, tmpl, use, want, operands); + if (!candidate.specialization) { + continue; + } + } else if (getTarget(ctx, entry.returned) != want) { + continue; + } + if (!best.rule || isBetter(sema, candidate, best, use->getBeginLoc(), + operands.size())) { + best = candidate; + } + } + }; + consider(key); + if (!key.empty()) { + consider(""); + } + + log() << "search expr '" << key << "', result:\n"; + if (!best.rule) { + log() << "None\n"; + return {}; + } + best.rule->dump(); + if (!best.tmpl) { + return {best.rule, {}}; + } + if (init_type && !best.init_type.isNull()) { + Trial trial(sema); + clang::Sema::InstantiatingTemplate instantiating(sema, use->getBeginLoc(), + best.specialization); + *init_type = sema.SubstType( + best.init_type, sema.getTemplateInstantiationArgs(best.specialization), + use->getBeginLoc(), clang::DeclarationName()); + } + return {best.rule, + getBindings( + best.tmpl->getTemplateParameters(), + best.specialization->getTemplateSpecializationArgs()->asArray())}; +} + +bool isInstanceOf(clang::Sema &sema, const clang::TypeAliasTemplateDecl *a, + const clang::TypeAliasTemplateDecl *b) { + auto *params = b->getTemplateParameters(); + llvm::SmallVector deduced(params->size()); + clang::sema::TemplateDeductionInfo info(a->getLocation()); + clang::TemplateArgument pattern(b->getTemplatedDecl()->getUnderlyingType()); + clang::TemplateArgument actual( + a->getTemplatedDecl()->getUnderlyingType().getCanonicalType()); + return sema.DeduceTemplateArguments(params, pattern, actual, info, deduced, + true) == + clang::TemplateDeductionResult::Success; +} + +TypeMatch findMemberType(clang::ASTContext &ctx, clang::QualType type) { + auto &sema = RuleDecls::GetSema(); + while (const auto *typedef_type = type->getAs()) { + const auto *decl = typedef_type->getDecl(); + if (const auto *owner = + llvm::dyn_cast( + decl->getDeclContext())) { + for (auto &[_, entry] : + RuleDecls::MemberTypeCandidates(decl->getName().str())) { + const auto *alias = + llvm::cast(entry.decl); + const auto *member = alias->getTemplatedDecl() + ->getUnderlyingType() + ->getAs(); + const auto *pattern = member->getQualifier().getAsType(); + if (!pattern) { + continue; + } + auto *params = alias->getTemplateParameters(); + llvm::SmallVector deduced( + params->size()); + clang::sema::TemplateDeductionInfo info(alias->getLocation()); + Trial trial(sema); + if (sema.DeduceTemplateArguments( + params, clang::TemplateArgument(clang::QualType(pattern, 0)), + clang::TemplateArgument(ctx.getCanonicalTagType(owner)), info, + deduced, true) == clang::TemplateDeductionResult::Success) { + return {entry.rule, + getBindings(params, std::vector( + deduced.begin(), deduced.end()))}; + } + } } + type = typedef_type->desugar(); } - return mapped; + return {}; +} + +TypeMatch findType(clang::ASTContext &ctx, clang::QualType type) { + if (auto match = findMemberType(ctx, type); match.first) { + return match; + } + if (auto *rule = RuleDecls::FindPlainType(type)) { + return {rule, {}}; + } + + auto &sema = RuleDecls::GetSema(); + auto canonical = type.getCanonicalType().getUnqualifiedType(); + TranslationRule::TypeRule *best = nullptr; + const clang::TypeAliasTemplateDecl *best_alias = nullptr; + Bindings best_bindings; + auto consider = [&](const std::string &bucket) { + for (auto &[_, entry] : RuleDecls::TypeCandidates(bucket)) { + if (const auto *function = + llvm::dyn_cast(entry.decl)) { + if (best) { + continue; + } + auto *tmpl = const_cast(function); + Trial trial(sema); + clang::OpaqueValueExpr operand(tmpl->getLocation(), + ctx.getPointerType(canonical), + clang::VK_PRValue); + clang::TemplateDeductionResult result; + if (auto *specialization = deduce(sema, tmpl, nullptr, {&operand}, + tmpl->getLocation(), result)) { + best = entry.rule; + best_bindings = getBindings( + tmpl->getTemplateParameters(), + specialization->getTemplateSpecializationArgs()->asArray()); + } + continue; + } + auto *alias = const_cast( + llvm::cast(entry.decl)); + Trial trial(sema); + clang::sema::TemplateDeductionInfo info(alias->getLocation()); + if (sema.DeduceTemplateArgumentsFromType(alias, canonical, info) != + clang::TemplateDeductionResult::Success) { + continue; + } + auto *params = alias->getTemplateParameters(); + llvm::SmallVector deduced( + params->size()); + clang::TemplateArgument pattern( + alias->getTemplatedDecl()->getUnderlyingType()); + clang::TemplateArgument actual(canonical); + if (sema.DeduceTemplateArguments(params, pattern, actual, info, deduced, + true) != + clang::TemplateDeductionResult::Success) { + continue; + } + if (best_alias && !(isInstanceOf(sema, alias, best_alias) && + !isInstanceOf(sema, best_alias, alias))) { + continue; + } + best = entry.rule; + best_alias = alias; + best_bindings = getBindings(params, std::vector( + deduced.begin(), deduced.end())); + } + }; + auto key = RuleDecls::GetTypeKey(canonical); + consider(key); + if (!key.empty()) { + consider(""); + } + return {best, std::move(best_bindings)}; +} + +} // namespace + +clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) { + clang::QualType type; + findExpr(ctx, expr, &type); + assert(!type.isNull() && "expression must match a rule with an init value"); + return type; +} + +ExprMatch Find(clang::ASTContext &ctx, const clang::Expr *expr) { + return findExpr(ctx, expr, nullptr); +} + +TypeMatch Find(clang::ASTContext &ctx, clang::QualType type) { + auto match = findType(ctx, type); + log() << "search type " << Printer::ToString(ctx, type) + << ", result: " << (match.first ? match.first->type_info.type : "None") + << '\n'; + return match; +} + +bool HasRuleNamed(const clang::FunctionDecl *decl) { + return decl->getIdentifier() && + !RuleDecls::ExprCandidates(decl->getName().str()).empty(); +} + +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 -// type mapping. Each template parameter in `tgt_template` is replaced with its -// corresponding instantiated type from `types`. // // Example: -// types = { {"i32"} } // 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 +619,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 f5f9e0db47..81175613bc 100644 --- a/cpp2rust/converter/rules/matcher.h +++ b/cpp2rust/converter/rules/matcher.h @@ -8,31 +8,34 @@ #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); +clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr); + +bool HasRuleNamed(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 c0d048b42f..0dc433b168 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -20,43 +20,34 @@ namespace { Model model_ = Model::kUnsafe; bool translation_rules_loaded_ = false; -ExprRuleMap exprs_; // key -> ExprRule -TypeRuleMap types_; // key -> TypeRule +std::unordered_map exprs_; +std::unordered_map types_; -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)); +bool pointsToUserType(clang::QualType type) { + if (!type->isPointerType()) { + return false; + } + auto *tag = type->getPointeeType()->getAsTagDecl(); + return tag && IsUserDefinedDecl(tag); } 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"))); + assert(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'; continue; } - for (auto &[_, rule] : expr_rules) { - exprs_.emplace(Matcher::Key(rule), std::move(rule)); + auto rule_dir = path.filename().string(); + for (auto &[name, rule] : expr_rules) { + exprs_.emplace(rule_dir + '/' + name, std::move(rule)); } - 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); - } - } - types_.emplace(std::move(key), std::move(rule)); + for (auto &[name, rule] : type_rules) { + types_.emplace(rule_dir + '/' + name, std::move(rule)); } } } @@ -70,16 +61,16 @@ 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}; +TranslationRule::ExprRule *FindExprRule(const std::string &dir, + const std::string &name) { + auto it = exprs_.find(dir + '/' + name); + return it == exprs_.end() ? nullptr : &it->second; } -std::ranges::subrange -TypeCandidates(const std::string &key) { - auto [begin, end] = types_.equal_range(key); - return {begin, end}; +TranslationRule::TypeRule *FindTypeRule(const std::string &dir, + const std::string &name) { + auto it = types_.find(dir + '/' + name); + return it == types_.end() ? nullptr : &it->second; } Matcher::Match Search(clang::ASTContext &ctx, @@ -102,25 +93,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) { @@ -138,6 +122,9 @@ bool IsLibcPassthrough(clang::ASTContext &ctx, const clang::Expr *expr) { if (tgt_ir == nullptr || !tgt_ir->body.empty() || !tgt_ir->is_extern) { return false; } + if (const auto *call = clang::dyn_cast(expr)) { + expr = call->getCallee(); + } const auto *ref = clang::dyn_cast(expr->IgnoreParenImpCasts()); const auto *decl = ref != nullptr ? ref->getDecl() : nullptr; @@ -148,50 +135,6 @@ 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; diff --git a/cpp2rust/converter/rules/registry.h b/cpp2rust/converter/rules/registry.h index aaa65d03b1..b2884f0a17 100644 --- a/cpp2rust/converter/rules/registry.h +++ b/cpp2rust/converter/rules/registry.h @@ -18,15 +18,11 @@ #include "converter/translation_rule.h" namespace cpp2rust::RuleRegistry { -using ExprRuleMap = - std::unordered_multimap; -using TypeRuleMap = - std::unordered_multimap; +TranslationRule::ExprRule *FindExprRule(const std::string &dir, + const std::string &name); -std::ranges::subrange -ExprCandidates(const std::string &key); -std::ranges::subrange -TypeCandidates(const std::string &key); +TranslationRule::TypeRule *FindTypeRule(const std::string &dir, + const std::string &name); Matcher::Match Search(clang::ASTContext &ctx, const clang::Expr *expr); @@ -41,13 +37,8 @@ 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_decls.cpp b/cpp2rust/converter/rules/rule_decls.cpp new file mode 100644 index 0000000000..2ec340f9d7 --- /dev/null +++ b/cpp2rust/converter/rules/rule_decls.cpp @@ -0,0 +1,528 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "converter/rules/rule_decls.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "converter/printer.h" +#include "converter/rules/registry.h" +#include "logging.h" +#include "rules_prelude.h" + +namespace cpp2rust::RuleDecls { + +namespace { + +constexpr std::string_view kPrefix = "cpp2rust_rules_"; + +ExprRuleDeclMap exprs_; +TypeRuleDeclMap types_; +TypeRuleDeclMap member_types_; +std::unordered_map plain_types_; +std::unordered_map alias_types_; +clang::Sema *sema_ = nullptr; + +bool isRuleName(std::string_view name, char kind) { + return name.size() > 1 && name[0] == kind && + std::all_of(name.begin() + 1, name.end(), + [](char c) { return std::isdigit((unsigned char)c); }); +} + +struct RuleId { + std::string dir; + std::string name; +}; + +std::optional getRuleId(const clang::NamedDecl *decl) { + if (!decl->getIdentifier()) { + return std::nullopt; + } + std::string_view name = decl->getName(); + if (const auto *ns = + llvm::dyn_cast(decl->getDeclContext())) { + std::string_view dir = ns->getIdentifier() ? ns->getName() : ""; + if (!dir.starts_with(kPrefix)) { + return std::nullopt; + } + return RuleId{std::string(dir.substr(kPrefix.size())), std::string(name)}; + } + if (!name.starts_with(kPrefix)) { + return std::nullopt; + } + name.remove_prefix(kPrefix.size()); + auto split = name.rfind('_'); + if (split == std::string_view::npos) { + return std::nullopt; + } + return RuleId{std::string(name.substr(0, split)), + std::string(name.substr(split + 1))}; +} + +} // namespace + +const clang::Expr *GetReturned(const clang::FunctionDecl *decl) { + const auto *body = + llvm::dyn_cast_or_null(decl->getBody()); + if (!body || body->size() != 1) { + return nullptr; + } + const auto *ret = llvm::dyn_cast(*body->body_begin()); + const clang::Expr *returned = ret ? ret->getRetValue() : nullptr; + while (returned) { + const auto *construct = + llvm::dyn_cast(SkipImplicit(returned)); + if (!construct || !construct->isElidable()) { + break; + } + returned = construct->getArg(0); + } + return returned; +} + +namespace { + +std::string getOperatorKey(clang::OverloadedOperatorKind op) { + if (op == clang::OO_None) { + return {}; + } + return std::string("operator") + clang::getOperatorSpelling(op); +} + +std::string getNameKey(clang::DeclarationName name) { + switch (name.getNameKind()) { + case clang::DeclarationName::Identifier: + if (const auto *identifier = name.getAsIdentifierInfo()) { + return identifier->getName().str(); + } + return {}; + case clang::DeclarationName::CXXOperatorName: + return getOperatorKey(name.getCXXOverloadedOperator()); + default: + return {}; + } +} + +} // namespace + +std::string GetTypeKey(clang::QualType type) { + type = type.getNonReferenceType(); + if (const auto *tst = type->getAs()) { + if (const auto *tmpl = tst->getTemplateName().getAsTemplateDecl()) { + return getNameKey(tmpl->getDeclName()); + } + } + if (const auto *tag = type->getAsTagDecl()) { + return getNameKey(tag->getDeclName()); + } + return {}; +} + +std::string GetAliasKey(clang::QualType type) { + type = type.getUnqualifiedType(); + if (const auto *decltype_type = + llvm::dyn_cast(type.getTypePtr())) { + type = decltype_type->getUnderlyingType().getUnqualifiedType(); + } + if (const auto *typeof_type = + llvm::dyn_cast(type.getTypePtr())) { + type = typeof_type->desugar().getUnqualifiedType(); + } + if (const auto *typedef_type = type->getAs(); + typedef_type && type.getCanonicalType()->isBuiltinType()) { + return typedef_type->getDecl()->getName().str(); + } + if (const auto *predefined = type->getAs()) { + return predefined->getIdentifier()->getName().str(); + } + if (const auto *pointer = type->getAs()) { + auto pointee = pointer->getPointeeType(); + auto key = GetAliasKey(pointee); + if (key.empty()) { + return {}; + } + return std::string(pointee.isConstQualified() ? "const " : "") + + (pointee.isVolatileQualified() ? "volatile " : "") + key + " *"; + } + return {}; +} + +const clang::Expr *SkipImplicit(const clang::Expr *expr) { + while (true) { + expr = expr->IgnoreParens(); + if (const auto *full = llvm::dyn_cast(expr)) { + expr = full->getSubExpr(); + continue; + } + if (const auto *temporary = + llvm::dyn_cast(expr)) { + expr = temporary->getSubExpr(); + continue; + } + if (const auto *bind = llvm::dyn_cast(expr)) { + expr = bind->getSubExpr(); + continue; + } + const auto *cast = llvm::dyn_cast(expr); + if (!cast) { + return expr; + } + expr = cast->getSubExpr(); + } +} + +namespace { + +bool isDeclaredByRules(clang::ASTContext &ctx, const clang::Decl *decl) { + auto &src_mgr = ctx.getSourceManager(); + return IsRulesPrelude(src_mgr.getFilename( + src_mgr.getSpellingLoc(decl->getCanonicalDecl()->getLocation()))); +} + +bool refersToUndeclared(const clang::Expr *expr) { + expr = SkipImplicit(expr); + if (const auto *call = llvm::dyn_cast(expr)) { + if (const auto *lookup = llvm::dyn_cast( + call->getCallee()->IgnoreParenImpCasts())) { + return lookup->getName().isIdentifier() && lookup->getNumDecls() == 0; + } + if (const auto *callee = call->getDirectCallee()) { + return callee->isImplicit() && !callee->getBuiltinID(); + } + } + return false; +} + +bool refersToUndeclared(clang::ASTContext &ctx, clang::QualType type) { + while (type->isPointerType() || type->isReferenceType()) { + type = type->getPointeeType(); + } + const auto *tag = type->getAsTagDecl(); + return tag && isDeclaredByRules(ctx, tag); +} + +} // namespace + +std::string GetExprKey(clang::ASTContext &ctx, const clang::Expr *expr) { + expr = SkipImplicit(expr); + + if (llvm::isa(expr) && + expr->getBeginLoc().isMacroID()) { + return clang::Lexer::getImmediateMacroName( + expr->getBeginLoc(), ctx.getSourceManager(), ctx.getLangOpts()) + .str(); + } + if (const auto *op = llvm::dyn_cast(expr)) { + return getOperatorKey(op->getOperator()); + } + if (const auto *rewritten = + llvm::dyn_cast(expr)) { + return getOperatorKey( + clang::BinaryOperator::getOverloadedOperator(rewritten->getOperator())); + } + if (const auto *call = llvm::dyn_cast(expr)) { + const auto *callee = call->getCallee()->IgnoreParenImpCasts(); + if (const auto *ref = llvm::dyn_cast(callee); + ref && llvm::isa(ref->getDecl())) { + return getOperatorKey(clang::OO_Call); + } + return GetExprKey(ctx, callee); + } + if (const auto *overload = llvm::dyn_cast(expr)) { + return getNameKey(overload->getName()); + } + if (const auto *member = + llvm::dyn_cast(expr)) { + return getNameKey(member->getMember()); + } + if (const auto *ref = + llvm::dyn_cast(expr)) { + return getNameKey(ref->getDeclName()); + } + if (const auto *member = llvm::dyn_cast(expr)) { + return getNameKey(member->getMemberDecl()->getDeclName()); + } + if (const auto *ref = llvm::dyn_cast(expr)) { + return getNameKey(ref->getDecl()->getDeclName()); + } + if (const auto *unary = llvm::dyn_cast(expr)) { + if (unary->getSubExpr()->isTypeDependent()) { + return getOperatorKey( + clang::UnaryOperator::getOverloadedOperator(unary->getOpcode())); + } + return GetExprKey(ctx, unary->getSubExpr()); + } + if (const auto *binary = llvm::dyn_cast(expr)) { + return getOperatorKey( + clang::BinaryOperator::getOverloadedOperator(binary->getOpcode())); + } + if (llvm::isa(expr)) { + return getOperatorKey(clang::OO_Subscript); + } + if (const auto *construct = + llvm::dyn_cast(expr)) { + return GetTypeKey(construct->getTypeAsWritten()); + } + if (const auto *construct = llvm::dyn_cast(expr)) { + return getNameKey(construct->getConstructor()->getParent()->getDeclName()); + } + if (const auto *cast = llvm::dyn_cast(expr)) { + return GetTypeKey(cast->getTypeAsWritten()); + } + return {}; +} + +namespace { + +unsigned getGenericNumber(const clang::NamedDecl *param) { + auto name = param->getName(); + unsigned n = 0; + if (!name.consume_front("T") || name.getAsInteger(10, n)) { + return 0; + } + return n; +} + +clang::QualType getInitType(const clang::FunctionDecl *function) { + if (function->param_empty()) { + return {}; + } + const auto *expansion = function->parameters() + .back() + ->getType() + ->getAs(); + if (!expansion) { + return {}; + } + const 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 RuleId &id, const clang::FunctionDecl *function, + const TranslationRule::ExprRule &rule) { + bool declared[TranslationRule::kMaxGenerics + 1] = {false}; + if (const auto *tmpl = function->getDescribedFunctionTemplate()) { + for (const auto *param : *tmpl->getTemplateParameters()) { + auto n = getGenericNumber(param); + assert(n <= TranslationRule::kMaxGenerics && + "template placeholder exceeds kMaxGenerics"); + declared[n] = true; + } + } + for (size_t i = 0, e = rule.generics.size(); i < e; ++i) { + if (!declared[i + 1]) { + llvm::errs() << id.dir << "::" << id.name << ": generic T" << (i + 1) + << " declared but missing from src\n"; + llvm::report_fatal_error("Absent generic from src"); + } + } + if (rule.usesInit() && getInitType(function).isNull()) { + llvm::errs() << id.dir << "::" << id.name << '\n'; + llvm::report_fatal_error( + "Expr rule uses init but its src pack is not declared as Init"); + } +} + +void addExprRule(clang::ASTContext &ctx, const clang::NamedDecl *decl) { + auto id = getRuleId(decl); + if (!id || !isRuleName(id->name, 'f')) { + return; + } + if (const auto *var = llvm::dyn_cast(decl)) { + const auto *init = var->getInit(); + auto *rule = RuleRegistry::FindExprRule(id->dir, id->name); + if (var->isInvalidDecl() || !init || init->containsErrors() || !rule) { + log() << "rule " << id->dir << "::" << id->name << " does not apply\n"; + return; + } + auto key = GetExprKey(ctx, init); + log() << "rule " << id->dir << "::" << id->name << " -> '" << key << "'\n"; + exprs_.emplace(std::move(key), ExprRuleDecl{nullptr, init, rule, {}}); + return; + } + const clang::FunctionDecl *function = nullptr; + if (const auto *tmpl = llvm::dyn_cast(decl)) { + function = tmpl->getTemplatedDecl(); + } else { + function = llvm::dyn_cast(decl); + } + if (!function || !function->isThisDeclarationADefinition()) { + return; + } + const auto *returned = GetReturned(function); + if (decl->isInvalidDecl() || function->isInvalidDecl() || !returned || + returned->containsErrors() || refersToUndeclared(returned)) { + log() << "rule " << id->dir << "::" << id->name << " does not apply\n"; + return; + } + auto *rule = RuleRegistry::FindExprRule(id->dir, id->name); + if (!rule) { + log() << "rule " << id->dir << "::" << id->name << " has no target\n"; + return; + } + checkGenerics(*id, function, *rule); + auto key = GetExprKey(ctx, returned); + log() << "rule " << id->dir << "::" << id->name << " -> '" << key << "'\n"; + exprs_.emplace(std::move(key), + ExprRuleDecl{function, returned, rule, getInitType(function)}); +} + +void addTypeRule(clang::ASTContext &ctx, const clang::NamedDecl *decl) { + auto id = getRuleId(decl); + if (!id || !isRuleName(id->name, 't')) { + return; + } + if (llvm::isa(decl)) { + if (auto *rule = RuleRegistry::FindTypeRule(id->dir, id->name)) { + log() << "rule " << id->dir << "::" << id->name << " -> ''\n"; + types_.emplace("", TypeRuleDecl{decl, rule}); + } + return; + } + const clang::TypedefNameDecl *alias = nullptr; + if (const auto *tmpl = llvm::dyn_cast(decl)) { + alias = tmpl->getTemplatedDecl(); + } else { + alias = llvm::dyn_cast(decl); + } + if (!alias) { + return; + } + if (decl->isInvalidDecl() || alias->isInvalidDecl() || + alias->getUnderlyingType()->containsErrors() || + refersToUndeclared(ctx, alias->getUnderlyingType())) { + log() << "rule " << id->dir << "::" << id->name << " does not apply\n"; + return; + } + auto *rule = RuleRegistry::FindTypeRule(id->dir, id->name); + if (!rule) { + log() << "rule " << id->dir << "::" << id->name << " has no target\n"; + return; + } + if (!llvm::isa(decl)) { + log() << "rule " << id->dir << "::" << id->name << " is a plain type\n"; + auto add = [&](auto &types, auto key) { + auto [it, inserted] = types.try_emplace(std::move(key), rule); + if (!inserted && it->second != rule) { + llvm::errs() << "ERROR: duplicate type rule for C++ type '" + << Printer::ToString(ctx, alias->getUnderlyingType()) + << "': maps to both '" << it->second->type_info.type + << "' and '" << rule->type_info.type << "'\n"; + std::exit(EXIT_FAILURE); + } + }; + if (auto key = GetAliasKey(alias->getUnderlyingType()); !key.empty()) { + add(alias_types_, std::move(key)); + return; + } + add(plain_types_, alias->getUnderlyingType() + .getCanonicalType() + .getUnqualifiedType() + .getAsOpaquePtr()); + return; + } + if (const auto *member = + alias->getUnderlyingType()->getAs()) { + auto key = member->getIdentifier()->getName().str(); + log() << "rule " << id->dir << "::" << id->name << " -> member '" << key + << "'\n"; + member_types_.emplace(std::move(key), TypeRuleDecl{decl, rule}); + return; + } + auto key = GetTypeKey(alias->getUnderlyingType()); + log() << "rule " << id->dir << "::" << id->name << " -> '" << key << "'\n"; + types_.emplace(std::move(key), TypeRuleDecl{decl, rule}); +} + +void addRules(clang::ASTContext &ctx, const clang::DeclContext *context) { + for (const auto *child : context->decls()) { + if (const auto *ns = llvm::dyn_cast(child)) { + if (ns->getIdentifier() && ns->getName().starts_with(kPrefix)) { + addRules(ctx, ns); + } + continue; + } + if (const auto *named = llvm::dyn_cast(child)) { + addExprRule(ctx, named); + addTypeRule(ctx, named); + } + } +} + +} // namespace + +clang::Sema &GetSema() { + assert(sema_); + return *sema_; +} + +void Collect(clang::Sema &sema) { + auto &ctx = sema.Context; + sema_ = &sema; + exprs_.clear(); + types_.clear(); + member_types_.clear(); + plain_types_.clear(); + alias_types_.clear(); + addRules(ctx, ctx.getTranslationUnitDecl()); + log() << "collected " << exprs_.size() << " expression rules and " + << types_.size() + plain_types_.size() + alias_types_.size() + << " type rules\n"; +} + +TranslationRule::TypeRule *FindPlainType(clang::QualType type) { + for (auto alias = type;;) { + auto key = GetAliasKey(alias); + if (key.empty()) { + break; + } + if (auto it = alias_types_.find(key); it != alias_types_.end()) { + return it->second; + } + const auto *typedef_type = alias->getAs(); + if (!typedef_type) { + break; + } + alias = typedef_type->desugar(); + } + auto it = plain_types_.find( + type.getCanonicalType().getUnqualifiedType().getAsOpaquePtr()); + return it == plain_types_.end() ? nullptr : it->second; +} + +std::ranges::subrange +ExprCandidates(const std::string &key) { + auto [begin, end] = exprs_.equal_range(key); + return {begin, end}; +} + +std::ranges::subrange +MemberTypeCandidates(const std::string &key) { + auto [begin, end] = member_types_.equal_range(key); + return {begin, end}; +} + +std::ranges::subrange +TypeCandidates(const std::string &key) { + auto [begin, end] = types_.equal_range(key); + return {begin, end}; +} + +} // namespace cpp2rust::RuleDecls diff --git a/cpp2rust/converter/rules/rule_decls.h b/cpp2rust/converter/rules/rule_decls.h new file mode 100644 index 0000000000..d31f61d79e --- /dev/null +++ b/cpp2rust/converter/rules/rule_decls.h @@ -0,0 +1,57 @@ +#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 "converter/translation_rule.h" + +namespace cpp2rust::RuleDecls { +struct ExprRuleDecl { + const clang::FunctionDecl *decl; + const clang::Expr *returned; + TranslationRule::ExprRule *rule; + clang::QualType init_type; +}; + +struct TypeRuleDecl { + const clang::NamedDecl *decl; + TranslationRule::TypeRule *rule; +}; + +using ExprRuleDeclMap = std::unordered_multimap; +using TypeRuleDeclMap = std::unordered_multimap; + +void Collect(clang::Sema &sema); + +clang::Sema &GetSema(); + +const clang::Expr *GetReturned(const clang::FunctionDecl *decl); + +const clang::Expr *SkipImplicit(const clang::Expr *expr); + +std::string GetExprKey(clang::ASTContext &ctx, const clang::Expr *expr); + +std::string GetTypeKey(clang::QualType type); + +std::string GetAliasKey(clang::QualType type); + +TranslationRule::TypeRule *FindPlainType(clang::QualType type); + +std::ranges::subrange +ExprCandidates(const std::string &key); + +std::ranges::subrange +TypeCandidates(const std::string &key); + +std::ranges::subrange +MemberTypeCandidates(const std::string &key); +} // namespace cpp2rust::RuleDecls diff --git a/cpp2rust/converter/rules/string_matcher.cpp b/cpp2rust/converter/rules/string_matcher.cpp deleted file mode 100644 index 66cc3b9809..0000000000 --- a/cpp2rust/converter/rules/string_matcher.cpp +++ /dev/null @@ -1,396 +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(const std::string &cpp_type) { - auto [rule, subs] = searchType(cpp_type); - 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; - } - 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/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 34c7696c96..ac319282d2 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -184,59 +184,6 @@ void LoadTgtFromIR(ExprRules &exprs, TypeRules &types, } } -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 +258,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 +282,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 +297,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()) { @@ -415,18 +318,6 @@ std::pair Load(const std::filesystem::path &dir, } } - LoadIrSrc(exprs, types, dir / "ir_src.json"); - - for (auto &[name, rule] : exprs) { - rule.validate(name); - } - 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"); - } - } return {std::move(exprs), std::move(types)}; } diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 8d6846ef8e..07fbf49774 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -72,16 +72,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,25 +81,14 @@ 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; diff --git a/cpp2rust/cpp2rust.cpp b/cpp2rust/cpp2rust.cpp index d8ffcb7472..7631b877cf 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; } } diff --git a/cpp2rust/cpp2rust_lib.cpp b/cpp2rust/cpp2rust_lib.cpp index 48754e5cf3..8271742d3d 100644 --- a/cpp2rust/cpp2rust_lib.cpp +++ b/cpp2rust/cpp2rust_lib.cpp @@ -8,11 +8,14 @@ #include #include +#include +#include #include "compat/platform_flags.h" #include "converter/converter.h" #include "converter/models/converter_refcount.h" #include "frontend_action.h" +#include "rules_prelude.h" namespace cpp2rust { std::string TranspileSrc(std::string_view cc_code, Model model, @@ -20,17 +23,23 @@ std::string TranspileSrc(std::string_view cc_code, Model model, const std::string &rules_dir, std::string_view filename) { auto tool_args = getPlatformClangBeginFlags(); + tool_args.push_back("-fno-spell-checking"); tool_args.push_back("-fparse-all-comments"); tool_args.insert(tool_args.end(), cxx_flags.begin(), cxx_flags.end()); auto end_flags = getPlatformClangEndFlags(); tool_args.insert(tool_args.end(), end_flags.begin(), end_flags.end()); + auto language = GetRulesLanguage(filename); + auto code = std::string(cc_code) + GetRulesPreludeInclude(language); + std::string rs_code; clang::tooling::runToolOnCodeWithArgs( std::make_unique(rs_code, model, /*first=*/true, rules_dir), - cc_code, tool_args, std::filesystem::path(filename).filename().string(), - filename.ends_with(".c") ? CLANG_C_COMPILER : CLANG_CXX_COMPILER); + code, tool_args, std::filesystem::path(filename).filename().string(), + filename.ends_with(".c") ? CLANG_C_COMPILER : CLANG_CXX_COMPILER, + std::make_shared(), + {{GetRulesPreludePath(language), BuildRulesPrelude(language)}}); Converter::EmitOpaqueRecords(rs_code); Converter::EmitVirtualMethods(rs_code); if (model == Model::kRefCount) { @@ -55,9 +64,10 @@ std::string TranspileDir(std::string_view build_dir, Model model, } clang::tooling::ClangTool Tool(*compile_dbase, files); + auto begin_flags = getPlatformClangBeginFlags(); + begin_flags.push_back("-fno-spell-checking"); Tool.appendArgumentsAdjuster(clang::tooling::getInsertArgumentAdjuster( - getPlatformClangBeginFlags(), - clang::tooling::ArgumentInsertPosition::BEGIN)); + begin_flags, clang::tooling::ArgumentInsertPosition::BEGIN)); Tool.appendArgumentsAdjuster(clang::tooling::getInsertArgumentAdjuster( getPlatformClangEndFlags(), clang::tooling::ArgumentInsertPosition::END)); // Redefine __FILE__ to use just the basename, so the generated code @@ -73,6 +83,28 @@ std::string TranspileDir(std::string_view build_dir, Model model, return result; }); + std::vector mapped; + mapped.reserve(2 * compile_dbase->getAllCompileCommands().size() + 4); + auto map = [&](std::string path, std::string content) { + mapped.push_back(std::move(path)); + mapped.push_back(std::move(content)); + Tool.mapVirtualFile(mapped[mapped.size() - 2], mapped.back()); + }; + for (auto language : {RulesLanguage::kC, RulesLanguage::kCxx}) { + map(GetRulesPreludePath(language), BuildRulesPrelude(language)); + } + for (const auto &compile_command : compile_dbase->getAllCompileCommands()) { + std::filesystem::path path(compile_command.Filename); + if (path.is_relative()) { + path = std::filesystem::path(compile_command.Directory) / path; + } + std::ifstream file(path); + std::string content{std::istreambuf_iterator(file), + std::istreambuf_iterator()}; + map(path.string(), + content + GetRulesPreludeInclude(GetRulesLanguage(path.string()))); + } + std::string rs_code; FrontendActionFactory factory(rs_code, model, rules_dir); Tool.run(&factory); diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp deleted file mode 100644 index cff775b803..0000000000 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ /dev/null @@ -1,1132 +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("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(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) { - auto flags = getPlatformClangBeginFlags(); - 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(), - src_path.extension() == ".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/cpp2rust/frontend_action.h b/cpp2rust/frontend_action.h index 5aae8a0250..d018de99d5 100644 --- a/cpp2rust/frontend_action.h +++ b/cpp2rust/frontend_action.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -13,8 +15,34 @@ #include "ast_consumer.h" #include "converter/factory.h" +#include "rules_prelude.h" namespace cpp2rust { +class SilenceRulesDiagnostics : public clang::PPCallbacks { +public: + explicit SilenceRulesDiagnostics(clang::CompilerInstance &CI) : CI_(CI) {} + + void LexedFileChanged(clang::FileID FID, LexedFileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, + clang::FileID PrevFID, + clang::SourceLocation Loc) override { + auto &src_mgr = CI_.getSourceManager(); + auto is_rules = [&](clang::FileID id) { + auto file = src_mgr.getFileEntryRefForID(id); + return file && IsRulesPrelude(file->getName()); + }; + if (Reason == LexedFileChangeReason::EnterFile && is_rules(FID)) { + CI_.getDiagnostics().setSuppressAllDiagnostics(true); + } + if (Reason == LexedFileChangeReason::ExitFile && is_rules(PrevFID)) { + CI_.getDiagnostics().setSuppressAllDiagnostics(false); + } + } + +private: + clang::CompilerInstance &CI_; +}; + class FrontendAction : public clang::ASTFrontendAction { public: explicit FrontendAction(std::string &rs_code, Model model, bool first, @@ -29,6 +57,12 @@ class FrontendAction : public clang::ASTFrontendAction { rules_dir_); } + bool BeginSourceFileAction(clang::CompilerInstance &CI) override { + CI.getPreprocessor().addPPCallbacks( + std::make_unique(CI)); + return true; + } + private: std::string &rs_code_; Model model_; diff --git a/cpp2rust/rules_prelude.cpp b/cpp2rust/rules_prelude.cpp new file mode 100644 index 0000000000..acf0daddc6 --- /dev/null +++ b/cpp2rust/rules_prelude.cpp @@ -0,0 +1,154 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include "rules_prelude.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace cpp2rust { + +namespace { + +std::string readFile(const fs::path &path) { + std::ifstream file(path); + return {std::istreambuf_iterator(file), + std::istreambuf_iterator()}; +} + +std::vector splitLines(const std::string &text) { + std::vector lines; + std::istringstream stream(text); + for (std::string line; std::getline(stream, line);) { + lines.push_back(std::move(line)); + } + return lines; +} + +std::string_view trimLeft(std::string_view text) { + while (!text.empty() && std::isspace((unsigned char)text.front())) { + text.remove_prefix(1); + } + return text; +} + +bool isInclude(std::string_view line) { + line = trimLeft(line); + if (!line.starts_with('#')) { + return false; + } + return trimLeft(line.substr(1)).starts_with("include"); +} + +std::string withoutIncludes(const std::string &text) { + std::string out; + for (const auto &line : splitLines(text)) { + if (!isInclude(line)) { + out += line; + } + out += '\n'; + } + return out; +} + +std::set getRuleNames(const std::string &text) { + std::set names; + auto is_word = [](char c) { + return std::isalnum((unsigned char)c) || c == '_'; + }; + for (size_t i = 0; i < text.size();) { + if (!is_word(text[i])) { + ++i; + continue; + } + size_t end = i; + while (end < text.size() && is_word(text[end])) { + ++end; + } + std::string_view word(text.data() + i, end - i); + if (word.size() > 1 && (word[0] == 'f' || word[0] == 't') && + std::all_of(word.begin() + 1, word.end(), + [](char c) { return std::isdigit((unsigned char)c); })) { + names.emplace(word); + } + i = end; + } + return names; +} + +void addCxxRules(std::string &out, const fs::path &rule_dir) { + out += std::format("namespace cpp2rust_rules_{} {{\n", + rule_dir.filename().string()); + out += withoutIncludes(readFile(rule_dir / "src.cpp")); + out += withoutIncludes(readFile(rule_dir / "src.c")); + out += "}\n"; +} + +void addPrefixedRules(std::string &out, const std::string &prefix, + const std::string &text) { + auto names = getRuleNames(text); + for (const auto &name : names) { + out += std::format("#define {} {}_{}\n", name, prefix, name); + } + out += withoutIncludes(text); + for (const auto &name : names) { + out += std::format("#undef {}\n", name); + } +} + +void addCRules(std::string &out, const fs::path &rule_dir) { + auto prefix = "cpp2rust_rules_" + rule_dir.filename().string(); + addPrefixedRules(out, prefix, readFile(rule_dir / "src.c")); +} + +} // namespace + +RulesLanguage GetRulesLanguage(std::string_view filename) { + return filename.ends_with(".c") ? RulesLanguage::kC : RulesLanguage::kCxx; +} + +std::string GetRulesPreludePath(RulesLanguage language) { + return std::string(RULES_SOURCE_DIR) + (language == RulesLanguage::kC + ? "/cpp2rust_rules_prelude.c" + : "/cpp2rust_rules_prelude.cpp"); +} + +bool IsRulesPrelude(std::string_view path) { + return path == GetRulesPreludePath(RulesLanguage::kC) || + path == GetRulesPreludePath(RulesLanguage::kCxx); +} + +std::string GetRulesPreludeInclude(RulesLanguage language) { + return std::format("\n#include \"{}\"\n", GetRulesPreludePath(language)); +} + +std::string BuildRulesPrelude(RulesLanguage language) { + std::vector rule_dirs; + for (const auto &entry : fs::directory_iterator(RULES_SOURCE_DIR)) { + if (entry.is_directory()) { + rule_dirs.push_back(entry.path()); + } + } + std::sort(rule_dirs.begin(), rule_dirs.end()); + + std::string out = "#pragma GCC system_header\n"; + for (const auto &rule_dir : rule_dirs) { + if (language == RulesLanguage::kCxx) { + addCxxRules(out, rule_dir); + } else { + addCRules(out, rule_dir); + } + } + return out; +} + +} // namespace cpp2rust diff --git a/cpp2rust/rules_prelude.h b/cpp2rust/rules_prelude.h new file mode 100644 index 0000000000..e4c370166c --- /dev/null +++ b/cpp2rust/rules_prelude.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 + +namespace cpp2rust { + +enum class RulesLanguage { kC, kCxx }; + +RulesLanguage GetRulesLanguage(std::string_view filename); + +std::string GetRulesPreludePath(RulesLanguage language); + +std::string BuildRulesPrelude(RulesLanguage language); + +bool IsRulesPrelude(std::string_view path); + +std::string GetRulesPreludeInclude(RulesLanguage language); + +} // namespace cpp2rust diff --git a/rules/algorithm/src.cpp b/rules/algorithm/src.cpp index 620934b32d..bad063e693 100644 --- a/rules/algorithm/src.cpp +++ b/rules/algorithm/src.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include template void f1(T1 first, T1 last) { @@ -30,15 +31,19 @@ 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 It, typename T1 = typename std::iterator_traits::value_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +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 ::value_type, + typename = typename std::enable_if::iterator>::value>::type> +void f12(It a0, It a1, const T2 &a2) { return std::fill(a0, a1, a2); } diff --git a/rules/arpa_inet/src.cpp b/rules/arpa_inet/src.c similarity index 100% rename from rules/arpa_inet/src.cpp rename to rules/arpa_inet/src.c diff --git a/rules/assert/src.cpp b/rules/assert/src.c similarity index 100% rename from rules/assert/src.cpp rename to rules/assert/src.c diff --git a/rules/brotli/src.cpp b/rules/brotli/src.c similarity index 94% rename from rules/brotli/src.cpp rename to rules/brotli/src.c index 534d59cc19..6f8c177968 100644 --- a/rules/brotli/src.cpp +++ b/rules/brotli/src.c @@ -1,14 +1,14 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -#include "brotli/decode.h" -#include "brotli/encode.h" - -using t1 = BrotliDecoderResult; -using t2 = BrotliEncoderMode; -using t3 = BrotliDecoderStateStruct *; -using t4 = const BrotliDecoderStateStruct *; -using t5 = BrotliDecoderErrorCode; +#include +#include + +typedef BrotliDecoderResult t1; +typedef BrotliEncoderMode t2; +typedef struct BrotliDecoderStateStruct *t3; +typedef const struct BrotliDecoderStateStruct *t4; +typedef BrotliDecoderErrorCode t5; BrotliEncoderMode f1() { return BROTLI_MODE_FONT; } diff --git a/rules/builtin/src.c b/rules/builtin/src.c index 805fbc1c5a..afe0231a9a 100644 --- a/rules/builtin/src.c +++ b/rules/builtin/src.c @@ -1,2 +1,42 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +#if defined(__linux__) +#include +#elif !defined(__APPLE__) +#error "Unsupported platform" +#endif + +long f1(long exp, long c) { return __builtin_expect(exp, c); } +int f2(unsigned int x) { return __builtin_ctz(x); } +int f3(unsigned int x) { return __builtin_clz(x); } +#if defined(__linux__) +unsigned short f4(unsigned short x) { return bswap_16(x); } +unsigned int f5(unsigned int x) { return bswap_32(x); } +unsigned long long f6(unsigned long long x) { return bswap_64(x); } +#elif defined(__APPLE__) +unsigned short f4(unsigned short x) { return __builtin_bswap16(x); } +unsigned int f5(unsigned int x) { return __builtin_bswap32(x); } +unsigned long long f6(unsigned long long x) { return __builtin_bswap64(x); } +#endif +int f7(unsigned long x) { return __builtin_ctzl(x); } +int f8(unsigned long x) { return __builtin_popcountl(x); } int f12(long a, long b, long *r) { return __builtin_mul_overflow(a, b, r); } int f13(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); } +#if defined(__x86_64__) || defined(__i386__) +void f11(void) { return __builtin_ia32_pause(); } +#endif + +void *f14(void *dst, const void *src, size_t n) { + return __builtin_memcpy(dst, src, n); +} + +void f15() { return __builtin_abort(); } + +int f16(unsigned long x) { return __builtin_clzl(x); } + +float f17() { return __builtin_inff(); } + +float f18(const char *tagp) { return __builtin_nanf(tagp); } diff --git a/rules/builtin/src.cpp b/rules/builtin/src.cpp index caf010941e..ecf42baf90 100644 --- a/rules/builtin/src.cpp +++ b/rules/builtin/src.cpp @@ -1,42 +1,5 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -#include - -#if defined(__linux__) -#include -#elif !defined(__APPLE__) -#error "Unsupported platform" -#endif - -long f1(long exp, long c) { return __builtin_expect(exp, c); } -int f2(unsigned int x) { return __builtin_ctz(x); } -int f3(unsigned int x) { return __builtin_clz(x); } -#if defined(__linux__) -unsigned short f4(unsigned short x) { return bswap_16(x); } -unsigned int f5(unsigned int x) { return bswap_32(x); } -unsigned long long f6(unsigned long long x) { return bswap_64(x); } -#elif defined(__APPLE__) -unsigned short f4(unsigned short x) { return __builtin_bswap16(x); } -unsigned int f5(unsigned int x) { return __builtin_bswap32(x); } -unsigned long long f6(unsigned long long x) { return __builtin_bswap64(x); } -#endif -int f7(unsigned long x) { return __builtin_ctzl(x); } -int f8(unsigned long x) { return __builtin_popcountl(x); } bool f9(long a, long b, long *r) { return __builtin_mul_overflow(a, b, r); } bool f10(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); } -#if defined(__x86_64__) || defined(__i386__) -void f11(void) { return __builtin_ia32_pause(); } -#endif - -void *f14(void *dst, const void *src, size_t n) { - return __builtin_memcpy(dst, src, n); -} - -void f15() { return __builtin_abort(); } - -int f16(unsigned long x) { return __builtin_clzl(x); } - -float f17() { return __builtin_inff(); } - -float f18(const char *tagp) { return __builtin_nanf(tagp); } diff --git a/rules/builtin_types/src.c b/rules/builtin_types/src.c new file mode 100644 index 0000000000..e32961311a --- /dev/null +++ b/rules/builtin_types/src.c @@ -0,0 +1,32 @@ +// 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; + +#ifndef __cplusplus +BUILTIN_TYPE(_Bool, t10, t11, t12, t13, t14) +#endif +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 diff --git a/rules/builtin_types/src.cpp b/rules/builtin_types/src.cpp index ee504e2130..cc858b186e 100644 --- a/rules/builtin_types/src.cpp +++ b/rules/builtin_types/src.cpp @@ -13,26 +13,9 @@ typedef decltype(nullptr) t0; BUILTIN_TYPE(1, bool) -BUILTIN_TYPE(2, char) -BUILTIN_TYPE(3, signed char) -BUILTIN_TYPE(4, unsigned char) BUILTIN_TYPE(5, char8_t) -BUILTIN_TYPE(6, short) -BUILTIN_TYPE(7, unsigned short) BUILTIN_TYPE(8, char16_t) -BUILTIN_TYPE(9, int) -BUILTIN_TYPE(10, unsigned int) BUILTIN_TYPE(11, wchar_t) BUILTIN_TYPE(12, char32_t) -BUILTIN_TYPE(13, float) -BUILTIN_TYPE(14, long) -BUILTIN_TYPE(15, unsigned long) -BUILTIN_TYPE(16, long long) -BUILTIN_TYPE(17, unsigned long long) -BUILTIN_TYPE(18, double) -BUILTIN_TYPE(19, long double) -BUILTIN_TYPE(20, __int128) -BUILTIN_TYPE(21, unsigned __int128) -BUILTIN_TYPE(22, void) #undef BUILTIN_TYPE diff --git a/rules/cstddef/src.c b/rules/cstddef/src.c new file mode 100644 index 0000000000..35b37dfab5 --- /dev/null +++ b/rules/cstddef/src.c @@ -0,0 +1,13 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include + +typedef size_t t2; +typedef size_t *t3; +typedef const size_t *t4; +typedef __typeof__(sizeof(0)) t5; +typedef ssize_t t6; +typedef ssize_t *t7; +typedef const ssize_t *t8; diff --git a/rules/cstddef/src.cpp b/rules/cstddef/src.cpp index b53725844b..53c3152935 100644 --- a/rules/cstddef/src.cpp +++ b/rules/cstddef/src.cpp @@ -2,18 +2,16 @@ // Distributed under the MIT license that can be found in the LICENSE file. #include -#include +#if __cplusplus >= 201703L using t1 = std::byte; +#endif -typedef size_t t2; -typedef size_t *t3; -typedef const size_t *t4; -typedef decltype(sizeof(0)) t5; -typedef ssize_t t6; -typedef ssize_t *t7; -typedef const ssize_t *t8; +using t2 = std::size_t; +using t3 = std::size_t *; +using t4 = const std::size_t *; +#if __cplusplus >= 201703L std::byte f1(const std::byte &a0, unsigned a1) { return operator<<(a0, a1); } std::byte f2(const std::byte &a0, unsigned a1) { return operator>>(a0, a1); } @@ -21,3 +19,4 @@ std::byte f2(const std::byte &a0, unsigned a1) { return operator>>(a0, a1); } std::byte f3(std::byte &a0, unsigned a1) { return operator<<=(a0, a1); } std::byte f4(std::byte &a0, unsigned a1) { return operator>>=(a0, a1); } +#endif diff --git a/rules/cstdlib/src.cpp b/rules/cstdlib/src.c similarity index 94% rename from rules/cstdlib/src.cpp rename to rules/cstdlib/src.c index cc8f44337c..5c75eaf711 100644 --- a/rules/cstdlib/src.cpp +++ b/rules/cstdlib/src.c @@ -1,9 +1,9 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -#include +#include -void f1() { return std::abort(); } +void f1() { return abort(); } void f2(void *a0) { return free(a0); } diff --git a/rules/dirent/src.cpp b/rules/dirent/src.c similarity index 86% rename from rules/dirent/src.cpp rename to rules/dirent/src.c index f5b720e13e..75c18fed2b 100644 --- a/rules/dirent/src.cpp +++ b/rules/dirent/src.c @@ -3,8 +3,8 @@ #include -using t1 = DIR *; -using t2 = struct dirent; +typedef DIR *t1; +typedef struct dirent t2; DIR *f1(const char *name) { return opendir(name); } diff --git a/rules/fcntl/src.cpp b/rules/fcntl/src.c similarity index 67% rename from rules/fcntl/src.cpp rename to rules/fcntl/src.c index ac5fb3351c..f23c71aec7 100644 --- a/rules/fcntl/src.cpp +++ b/rules/fcntl/src.c @@ -1,17 +1,12 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#define _GNU_SOURCE #include -template -int f1(int a0, int a1, Args... args) { - return fcntl(a0, a1, args...); -} +int (*f1)(int, int, ...) = fcntl; -template -int f2(const char *a0, int a1, Args... args) { - return open(a0, a1, args...); -} +int (*f2)(const char *, int, ...) = open; int f3(void) { return O_CREAT; } int f4(void) { return O_TRUNC; } diff --git a/rules/fnmatch/src.cpp b/rules/fnmatch/src.c similarity index 100% rename from rules/fnmatch/src.cpp rename to rules/fnmatch/src.c diff --git a/rules/ifaddrs/src.cpp b/rules/ifaddrs/src.c similarity index 100% rename from rules/ifaddrs/src.cpp rename to rules/ifaddrs/src.c diff --git a/rules/ioctl/src.cpp b/rules/ioctl/src.c similarity index 55% rename from rules/ioctl/src.cpp rename to rules/ioctl/src.c index 02587ce47c..fe322aec32 100644 --- a/rules/ioctl/src.cpp +++ b/rules/ioctl/src.c @@ -3,7 +3,4 @@ #include -template -int f1(int a0, unsigned long a1, Args... args) { - return ioctl(a0, a1, args...); -} +int (*f1)(int, unsigned long, ...) = ioctl; diff --git a/rules/iomanip/src.cpp b/rules/iomanip/src.cpp index 3140233256..0b6049574b 100644 --- a/rules/iomanip/src.cpp +++ b/rules/iomanip/src.cpp @@ -3,4 +3,4 @@ #include -auto f1(int n) { return std::setw(n); } +decltype(std::setw(0)) f1(int n) { return std::setw(n); } diff --git a/rules/ip/src.cpp b/rules/ip/src.c similarity index 100% rename from rules/ip/src.cpp rename to rules/ip/src.c diff --git a/rules/locale/src.cpp b/rules/locale/src.c similarity index 100% rename from rules/locale/src.cpp rename to rules/locale/src.c diff --git a/rules/map/src.cpp b/rules/map/src.cpp index 121a140511..1a81480c01 100644 --- a/rules/map/src.cpp +++ b/rules/map/src.cpp @@ -1,16 +1,30 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#include #include +#include #include template using t1 = std::map; -template -using t2 = typename std::map::const_iterator; - -template -using t3 = typename std::map::iterator; +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +void t2(It *); + +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +void t3(It *); template T2 &f1(std::map &o, const T1 &key) { return o.operator[](key); @@ -53,9 +67,14 @@ typename std::map::iterator f10(std::map &o, const T1 &key) { return o.find(key); } -template -bool f11(typename std::map::iterator a, - typename std::map::iterator b) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +bool f11(It a, It b) { return operator!=(a, b); } @@ -64,9 +83,14 @@ typename std::map::iterator f12(std::map &o) { return o.begin(); } -template -bool f13(typename std::map::const_iterator a, - typename std::map::const_iterator b) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +bool f13(It a, It b) { return operator==(a, b); } @@ -80,9 +104,14 @@ const T2 &f15(const std::map &o, const T1 &key) { return o.at(key); } -template -bool f16(typename std::map::iterator a, - typename std::map::iterator b) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +bool f16(It a, It b) { return operator==(a, b); } @@ -92,29 +121,58 @@ typename std::map::const_iterator f17(const std::map &o, return o.find(key); } -template -typename std::map::const_iterator -f19(const typename std::map::iterator &it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +typename std::map::const_iterator f19(const It &it) { return typename std::map::const_iterator(it); } -template -const T1 &f20(typename std::map::const_iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +const T1 &f20(It it) { return it->first; } -template -const T2 &f21(typename std::map::const_iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if::const_iterator>::value>::type> +const T2 &f21(It it) { return it->second; } -template -const T1 &f22(typename std::map::iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +const T1 &f22(It it) { return it->first; } -template -T2 &f23(typename std::map::iterator it) { +template < + typename It, + typename T1 = typename std::remove_const< + typename std::iterator_traits::value_type::first_type>::type, + typename T2 = typename std::iterator_traits::value_type::second_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +T2 &f23(It it) { return it->second; } diff --git a/rules/math/src.cpp b/rules/math/src.c similarity index 100% rename from rules/math/src.cpp rename to rules/math/src.c diff --git a/rules/net_if/src.cpp b/rules/net_if/src.c similarity index 100% rename from rules/net_if/src.cpp rename to rules/net_if/src.c diff --git a/rules/netdb/src.cpp b/rules/netdb/src.c similarity index 95% rename from rules/netdb/src.cpp rename to rules/netdb/src.c index ad0ccd2e0f..c49b27510e 100644 --- a/rules/netdb/src.cpp +++ b/rules/netdb/src.c @@ -1,6 +1,7 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#define _GNU_SOURCE #include typedef struct addrinfo t1; diff --git a/rules/new/src.cpp b/rules/new/src.cpp index ea1a12ed09..fe9fd99ccb 100644 --- a/rules/new/src.cpp +++ b/rules/new/src.cpp @@ -11,4 +11,6 @@ void *f3(std::size_t a0) { return ::operator new[](a0); } void f4(void *a0) { return ::operator delete[](a0); } +#if __cplusplus >= 201703L template T1 *f5(T1 *a0) { return std::launder(a0); } +#endif diff --git a/rules/pair/src.cpp b/rules/pair/src.cpp index dbf2fbf50a..635a130f51 100644 --- a/rules/pair/src.cpp +++ b/rules/pair/src.cpp @@ -31,14 +31,17 @@ std::pair f6(T3 &a0, T4 &a1) { template std::pair f7(T3 &&a0, T4 &&a1) { - return std::pair(std::move(a0), std::move(a1)); + return std::pair(std::forward(a0), std::forward(a1)); } -template auto f9(T1 &&a0, T2 &a1) { +template +auto f9(T1 &&a0, T2 &a1) -> decltype(std::make_pair(std::move(a0), a1)) { return std::make_pair(std::move(a0), a1); } -template auto f10(T1 &&a0, T2 &&a1) { +template +auto f10(T1 &&a0, T2 &&a1) + -> decltype(std::make_pair(std::move(a0), std::move(a1))) { return std::make_pair(std::move(a0), std::move(a1)); } diff --git a/rules/poll/src.cpp b/rules/poll/src.c similarity index 100% rename from rules/poll/src.cpp rename to rules/poll/src.c diff --git a/rules/pwd/src.cpp b/rules/pwd/src.c similarity index 94% rename from rules/pwd/src.cpp rename to rules/pwd/src.c index 12dbd3eec9..f612962f84 100644 --- a/rules/pwd/src.cpp +++ b/rules/pwd/src.c @@ -1,6 +1,7 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#define _GNU_SOURCE #include typedef struct passwd t1; diff --git a/rules/select/src.cpp b/rules/select/src.c similarity index 96% rename from rules/select/src.cpp rename to rules/select/src.c index 9e2bcee672..d9f5bb0759 100644 --- a/rules/select/src.cpp +++ b/rules/select/src.c @@ -3,7 +3,7 @@ #include -using t1 = fd_set; +typedef fd_set t1; int f1(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { diff --git a/rules/signal/src.cpp b/rules/signal/src.c similarity index 92% rename from rules/signal/src.cpp rename to rules/signal/src.c index 9ff7721822..618ab594dd 100644 --- a/rules/signal/src.cpp +++ b/rules/signal/src.c @@ -1,6 +1,7 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#define _GNU_SOURCE #include int f1(int signum, const struct sigaction *act, struct sigaction *oldact) { diff --git a/rules/stat/src.cpp b/rules/stat/src.c similarity index 95% rename from rules/stat/src.cpp rename to rules/stat/src.c index 5d79dfda38..eb642f227b 100644 --- a/rules/stat/src.cpp +++ b/rules/stat/src.c @@ -1,6 +1,7 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#define _GNU_SOURCE #include typedef struct stat t1; diff --git a/rules/stdio/src.cpp b/rules/stdio/src.c similarity index 92% rename from rules/stdio/src.cpp rename to rules/stdio/src.c index 271233d21d..1a2419033e 100644 --- a/rules/stdio/src.cpp +++ b/rules/stdio/src.c @@ -1,9 +1,10 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#define _GNU_SOURCE #include -using t1 = FILE *; +typedef FILE *t1; FILE *f1(const char *pathname, const char *mode) { return fopen(pathname, mode); @@ -57,10 +58,7 @@ int f19(FILE *stream, off_t offset, int whence) { FILE *f20(int fd, const char *mode) { return fdopen(fd, mode); } -template -int f21(char *a0, size_t a1, const char *a2, Args... args) { - return snprintf(a0, a1, a2, args...); -} +int (*f21)(char *, size_t, const char *, ...) = snprintf; int f22(const char *a0, const char *a1) { return rename(a0, a1); diff --git a/rules/string/src.cpp b/rules/string/src.cpp index 276962df85..ea6f3a3a26 100644 --- a/rules/string/src.cpp +++ b/rules/string/src.cpp @@ -23,7 +23,9 @@ std::string &f4(std::string &s, const char *p, std::size_t n) { const char *f5(const std::string &s) { return s.c_str(); } +#if __cplusplus >= 201703L char *f6(std::string &s) { return s.data(); } +#endif std::string f7(const char *s, std::size_t n) { return std::string(s, n); } diff --git a/rules/termios/src.cpp b/rules/termios/src.c similarity index 100% rename from rules/termios/src.cpp rename to rules/termios/src.c diff --git a/rules/unique_ptr/src.cpp b/rules/unique_ptr/src.cpp index 8f7c5458a3..042851c347 100644 --- a/rules/unique_ptr/src.cpp +++ b/rules/unique_ptr/src.cpp @@ -9,9 +9,11 @@ template using Init = A; template using t1 = std::unique_ptr; template using t2 = std::unique_ptr; -template std::unique_ptr f1(std::size_t n) { +#if __cplusplus >= 201402L +template std::unique_ptr f1(std::size_t n) { return std::make_unique(n); } +#endif template T1 *f2(std::unique_ptr &o) { return o.get(); } @@ -33,15 +35,21 @@ template void f6(std::unique_ptr &o, T1 *p) { template T1 *f7(std::unique_ptr &o) { return o.get(); } +#if __cplusplus >= 201402L template std::unique_ptr f8(Init &&...args) { return std::make_unique(std::forward(args)...); } +#endif template void f9(std::unique_ptr &o) { return o.reset(nullptr); } +template void f16(std::unique_ptr &o) { + return o.reset(nullptr); +} + template std::unique_ptr f10() { return std::unique_ptr(); } diff --git a/rules/unique_ptr/tgt_refcount.rs b/rules/unique_ptr/tgt_refcount.rs index 46600c01d3..d062ab77d6 100644 --- a/rules/unique_ptr/tgt_refcount.rs +++ b/rules/unique_ptr/tgt_refcount.rs @@ -75,3 +75,7 @@ fn f14(a0: Ptr>>, a1: &mut Option>) { fn f15(a0: Ptr>>>, a1: &mut Option>>) { a0.write(a1.take()) } + +fn f16(a0: Ptr>>) { + a0.write(None) +} diff --git a/rules/unique_ptr/tgt_unsafe.rs b/rules/unique_ptr/tgt_unsafe.rs index 72602f1634..c902a5182f 100644 --- a/rules/unique_ptr/tgt_unsafe.rs +++ b/rules/unique_ptr/tgt_unsafe.rs @@ -69,3 +69,7 @@ unsafe fn f14(a0: &mut Option>, a1: &mut Option>) { unsafe fn f15(a0: &mut Option>, a1: &mut Option>) { *a0 = a1.take() } + +unsafe fn f16(a0: &mut Option>) { + *a0 = None +} diff --git a/rules/unistd/src.cpp b/rules/unistd/src.c similarity index 97% rename from rules/unistd/src.cpp rename to rules/unistd/src.c index 7b0bd0f70a..b1a72ccbbb 100644 --- a/rules/unistd/src.cpp +++ b/rules/unistd/src.c @@ -1,6 +1,7 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. +#define _GNU_SOURCE #include int f1(int fd) { return close(fd); } diff --git a/rules/vector/src.cpp b/rules/vector/src.cpp index 0121a7e316..e530287dec 100644 --- a/rules/vector/src.cpp +++ b/rules/vector/src.cpp @@ -3,23 +3,45 @@ #include #include +#include +#include #include template using Init = A; template using t1 = std::vector; -template using t2 = typename std::vector::iterator; +template < + typename It, typename T1 = typename std::iterator_traits::value_type, + typename = typename std::enable_if< + std::is_same::iterator>::value>::type> +void t2(It *); template using t3 = std::vector>; -template using t4 = typename std::vector::const_iterator; +template ::value_type, + typename = typename std::enable_if::const_iterator>::value>::type> +void t4(It *); template > using t5 = std::vector; +template +using t8 = typename std::vector::size_type; + #if defined(__linux__) -template > -using t6 = typename std::vector::iterator; -template > -using t7 = typename std::vector::const_iterator; +template