Skip to content

Commit 29d6945

Browse files
authored
Merge branch 'master' into get
2 parents cd421d9 + 5865db2 commit 29d6945

36 files changed

Lines changed: 1097 additions & 88 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1846,11 +1846,16 @@ bool Converter::ConvertSpecialCall(clang::CallExpr *expr) {
18461846
return false;
18471847
}
18481848

1849+
bool Converter::ExprMapsValueToPointer(const clang::CallExpr *expr) const {
1850+
return Mapper::Contains(ctx_, expr) && !expr->getType()->isPointerType() &&
1851+
RuleRegistry::MapsToPointer(ctx_, expr->getType());
1852+
}
1853+
18491854
void Converter::EmitCallResult(clang::CallExpr *expr, std::string str,
18501855
const TempMaterializationCtx *temps) {
18511856
bool deref = (GetReturnTypeOfFunction(expr)->isReferenceType() ||
18521857
(temps && IsReferenceType(expr))) &&
1853-
!isAddrOf() && !isVoid();
1858+
!isAddrOf() && !isVoid() && !ExprMapsValueToPointer(expr);
18541859
// The dereference is in the block of the temporaries, so they are still
18551860
// alive, as is the read of a field of the result.
18561861
if (deref && !(temps && Mapper::IsRefExpr(ctx_, expr))) {
@@ -5283,7 +5288,7 @@ Converter::ConvertInitFragment(clang::Expr *expr,
52835288
if (!tgt_ir || !tgt_ir->usesInit()) {
52845289
llvm::report_fatal_error("expression rule does not use Init");
52855290
}
5286-
auto type = RuleRegistry::GetInitType(expr);
5291+
auto type = RuleRegistry::GetInitType(ctx_, expr);
52875292

52885293
Buffer buf(*this);
52895294
PushExprKind push(*this, ExprKind::RValue);

‎cpp2rust/converter/converter.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
899899

900900
virtual bool IsReferenceType(const clang::Expr *expr) const;
901901

902+
bool ExprMapsValueToPointer(const clang::CallExpr *expr) const;
903+
902904
virtual bool RecordDerivesDefault(const clang::RecordDecl *decl);
903905

904906
virtual bool TypeDerivesDefault(clang::QualType qual_type);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1214,7 +1214,7 @@ void ConverterRefCount::EmitCallResult(clang::CallExpr *expr, std::string str,
12141214
};
12151215
auto ty = GetReturnTypeOfFunction(expr);
12161216
if (auto *ref = ty->getAs<clang::ReferenceType>();
1217-
ref && !isAddrOf() && !isVoid()) {
1217+
ref && !isAddrOf() && !isVoid() && !ExprMapsValueToPointer(expr)) {
12181218
if (isLValue()) {
12191219
pending_deref_.set(wrap(std::move(str)), /*fresh=*/true);
12201220
return;

‎cpp2rust/converter/rules/registry.cpp‎

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -60,31 +60,13 @@ bool pointsToUserType(clang::QualType type) {
6060
return tag && IsUserDefinedDecl(tag);
6161
}
6262

63+
constexpr llvm::StringLiteral kInitAliasPrefix = "cpp2rust_init_";
64+
6365
bool isRuleName(llvm::StringRef name) {
6466
return name.size() > 1 && (name[0] == 'f' || name[0] == 't') &&
6567
llvm::all_of(name.drop_front(), llvm::isDigit);
6668
}
6769

68-
// The T of a rule's trailing `Init<T, Args> &&...args` pack.
69-
clang::QualType getInitPattern(const clang::FunctionDecl *rule) {
70-
if (rule->param_empty()) {
71-
return {};
72-
}
73-
auto expansion =
74-
rule->parameters().back()->getType()->getAs<clang::PackExpansionType>();
75-
if (!expansion) {
76-
return {};
77-
}
78-
auto alias = expansion->getPattern()
79-
.getNonReferenceType()
80-
->getAs<clang::TemplateSpecializationType>();
81-
if (!alias || !alias->isTypeAlias() ||
82-
alias->getTemplateName().getAsTemplateDecl()->getName() != "Init") {
83-
return {};
84-
}
85-
return alias->template_arguments()[0].getAsType();
86-
}
87-
8870
void collectGenerics(const std::vector<TranslationRule::BodyFragment> &body,
8971
std::unordered_set<unsigned> &used) {
9072
TranslationRule::AnyFragment(body, [&](const TranslationRule::BodyFragment
@@ -158,6 +140,7 @@ void checkGenerics(const std::string &dir, const std::string &name,
158140
for (const auto &param : rule.params) {
159141
collectGenerics(param.type, used);
160142
}
143+
collectGenerics(rule.init.type, used);
161144
for (unsigned i : used) {
162145
if (!declared.contains(std::format("T{}", i)) &&
163146
!isBoundArgument(rule, i)) {
@@ -166,10 +149,14 @@ void checkGenerics(const std::string &dir, const std::string &name,
166149
llvm::report_fatal_error("Absent generic from src");
167150
}
168151
}
169-
if (rule.usesInit() && getInitPattern(decl).isNull()) {
152+
if (rule.usesInit() &&
153+
(decl->param_empty() || !decl->parameters()
154+
.back()
155+
->getType()
156+
->getAs<clang::PackExpansionType>())) {
170157
llvm::errs() << dir << "::" << name << '\n';
171-
llvm::report_fatal_error("Expr rule uses init but its src pack is not "
172-
"declared as Init<T, Args>");
158+
llvm::report_fatal_error(
159+
"Expr rule uses init but its src does not end in a pack");
173160
}
174161
}
175162

@@ -219,6 +206,10 @@ void addCxxRules(clang::ASTContext &ctx, const clang::NamespaceDecl *rules) {
219206
}
220207
for (auto decl : ns->decls()) {
221208
auto named = llvm::cast<clang::NamedDecl>(decl);
209+
if (llvm::isa<clang::TypeAliasTemplateDecl>(named) &&
210+
named->getName().starts_with(kInitAliasPrefix)) {
211+
continue;
212+
}
222213
if (!isRuleName(named->getName())) {
223214
llvm::report_fatal_error("declaration is not a rule");
224215
}
@@ -451,18 +442,58 @@ clang::QualType GetConversionSourceType(clang::ASTContext &ctx,
451442
llvm::report_fatal_error("converted generic has no CppFrom bound");
452443
}
453444

454-
clang::QualType GetInitType(const clang::Expr *expr) {
445+
clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) {
455446
auto match = findExpr(expr);
456447
assert(match.rule_decl && "expression must have a translation rule");
448+
const auto &rule = match.rule_decl->rule;
449+
if (rule.init.cpp_type.empty()) {
450+
llvm::StringRef type = rule.init.type;
451+
unsigned n = 0;
452+
[[maybe_unused]] bool bad =
453+
!type.consume_front("T") || type.getAsInteger(10, n);
454+
assert(!bad && "init type must be a bare generic or a cpp_type!");
455+
return GetGenericType(ctx, expr, n);
456+
}
457457
auto function = match.rule_decl->decl->getAsFunction();
458-
auto pattern = getInitPattern(function);
459-
assert(!pattern.isNull() && "rule has no Init<T, Args> pack");
460-
clang::MultiLevelTemplateArgumentList args(
461-
function->getDescribedFunctionTemplate(), match.args, /*Final=*/false);
458+
auto tmpl = function->getDescribedFunctionTemplate();
459+
assert(tmpl && "cpp_type! init in a non-template rule");
460+
clang::TypeAliasTemplateDecl *alias = nullptr;
461+
for (auto decl : function->getDeclContext()->lookup(
462+
&ctx.Idents.get(InitAliasName(model_, function->getName().str())))) {
463+
if ((alias = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(decl))) {
464+
break;
465+
}
466+
}
467+
assert(alias && "cpp_type! init has no alias next to its rule");
468+
clang::MultiLevelTemplateArgumentList args(tmpl, match.args,
469+
/*Final=*/false);
462470
clang::Sema::InstantiatingTemplate inst(
463471
*sema_, expr->getBeginLoc(), const_cast<clang::FunctionDecl *>(function));
464-
return sema_->SubstType(pattern, args, expr->getBeginLoc(),
465-
clang::DeclarationName());
472+
auto type =
473+
sema_->SubstType(alias->getTemplatedDecl()->getUnderlyingType(), args,
474+
expr->getBeginLoc(), clang::DeclarationName());
475+
assert(!type.isNull() && "cpp_type! init does not substitute");
476+
return type;
477+
}
478+
479+
std::string InitCppType(Model model, const std::string &dir,
480+
const std::string &name) {
481+
auto rule = findRule(model, dir, name);
482+
if (!rule) {
483+
rule = findRule(Model::kUnsafe, dir, name);
484+
}
485+
if (!rule) {
486+
return {};
487+
}
488+
auto params = rule->getObject("params");
489+
auto init = params ? params->getObject("init") : nullptr;
490+
auto cpp_type = init ? init->getString("cpp_type") : std::nullopt;
491+
return cpp_type ? cpp_type->str() : std::string();
492+
}
493+
494+
std::string InitAliasName(Model model, const std::string &name) {
495+
return std::format("{}{}_{}", kInitAliasPrefix.str(),
496+
model == Model::kRefCount ? "refcount" : "unsafe", name);
466497
}
467498

468499
bool HasExprRule(const clang::FunctionDecl *decl) {

‎cpp2rust/converter/rules/registry.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ Matcher::Match<TranslationRule::TypeRule> Search(clang::ASTContext &ctx,
3737

3838
const TranslationRule::ExprRule *GetExprRule(clang::ASTContext &ctx,
3939
const clang::Expr *expr);
40-
clang::QualType GetInitType(const clang::Expr *expr);
40+
clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr);
41+
42+
std::string InitCppType(Model model, const std::string &dir,
43+
const std::string &name);
44+
45+
std::string InitAliasName(Model model, const std::string &name);
4146

4247
clang::QualType GetGenericType(clang::ASTContext &ctx, const clang::Expr *expr,
4348
unsigned n);

‎cpp2rust/converter/rules/rules_loader.cpp‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "converter/converter_lib.h"
2929
#include "converter/rules/decls.h"
30+
#include "converter/rules/registry.h"
3031
#include "logging.h"
3132

3233
namespace cpp2rust::RulesLoader {
@@ -314,6 +315,16 @@ const llvm::json::Object &CxxIndex() {
314315
std::string WrapRule(const llvm::json::Object &rule, bool is_cxx) {
315316
auto dir = rule.getString("dir")->str();
316317
auto text = rule.getString("text")->str();
318+
if (auto header = rule.getString("template_header")) {
319+
auto name = rule.getString("name")->str();
320+
for (auto model : {Model::kUnsafe, Model::kRefCount}) {
321+
auto cpp_type = RuleRegistry::InitCppType(model, dir, name);
322+
if (!cpp_type.empty()) {
323+
text += std::format("{} using {} = {};\n", header->str(),
324+
RuleRegistry::InitAliasName(model, name), cpp_type);
325+
}
326+
}
327+
}
317328
if (!is_cxx) {
318329
return std::format("#define {0} cpp2rust_rules_{1}_{0}\n{2}#undef {0}\n",
319330
rule.getString("name")->str(), dir, text);

‎cpp2rust/converter/translation_rule.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ TypeInfo ParseTypeInfoJSON(const llvm::json::Object &obj) {
2020
TypeInfo info;
2121
if (auto ty = obj.getString("type"))
2222
info.type = ty->str();
23+
if (auto cpp_type = obj.getString("cpp_type"))
24+
info.cpp_type = cpp_type->str();
2325
if (auto v = obj.getBoolean("is_refcount_pointer"))
2426
info.is_refcount_pointer = *v;
2527
if (auto v = obj.getBoolean("is_unsafe_pointer"))
@@ -296,6 +298,10 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) {
296298
if (auto *params = obj.getObject("params")) {
297299
for (auto &[key, val] : *params) {
298300
if (auto *param_obj = val.getAsObject()) {
301+
if (key == "init") {
302+
ir.init = ParseTypeInfoJSON(*param_obj);
303+
continue;
304+
}
299305
size_t id = atoi(llvm::StringRef(key).data() + 1);
300306
ir.params.resize(std::max(ir.params.size(), id + 1));
301307
ir.params[id] = ParseTypeInfoJSON(*param_obj);

‎cpp2rust/converter/translation_rule.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ bool AnyFragment(const std::vector<BodyFragment> &body,
9292
struct TypeInfo {
9393
std::vector<std::string> derives;
9494
std::string type;
95+
std::string cpp_type;
9596
bool is_refcount_pointer = false;
9697
bool is_unsafe_pointer = false;
9798

@@ -102,6 +103,7 @@ struct TypeInfo {
102103

103104
struct ExprRule {
104105
std::vector<TypeInfo> params;
106+
TypeInfo init;
105107
TypeInfo return_type;
106108
std::vector<std::vector<Bound>> generics; // "T1" -> [Ord, Clone]
107109
std::vector<BodyFragment> body;

‎cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,14 @@ void AddRequiredClasses(clang::QualType type,
7878
if (auto name = type->getAs<clang::DependentNameType>()) {
7979
auto qualifier = name->getQualifier();
8080
if (qualifier.getKind() == clang::NestedNameSpecifier::Kind::Type) {
81-
AddRequiredClasses(clang::QualType(qualifier.getAsType(), 0), required);
81+
if (auto spec = qualifier.getAsType()
82+
->getAs<clang::TemplateSpecializationType>()) {
83+
for (const auto &arg : spec->template_arguments()) {
84+
if (arg.getKind() == clang::TemplateArgument::Type) {
85+
AddRequiredClasses(arg.getAsType(), required);
86+
}
87+
}
88+
}
8289
}
8390
return;
8491
}
@@ -286,6 +293,14 @@ void IndexRuleFile(clang::ASTContext &ctx, const std::string &dir_name,
286293
auto range = clang::tooling::getExtendedRange(*decl, clang::tok::semi, ctx);
287294
auto text = clang::tooling::getText(range, ctx).str() + '\n';
288295
llvm::json::Object rule{{"dir", dir_name}, {"name", name}, {"text", text}};
296+
if (auto tmpl = llvm::dyn_cast<clang::FunctionTemplateDecl>(decl)) {
297+
rule["template_header"] =
298+
clang::tooling::getText(
299+
clang::CharSourceRange::getTokenRange(
300+
tmpl->getTemplateParameters()->getSourceRange()),
301+
ctx)
302+
.str();
303+
}
289304
if (any_class) {
290305
rule["requires"] = llvm::json::Array{RulesLoader::kAnyIteratorKey};
291306
} else if (auto required = RequiredClasses(decl, alias);

‎libcc2rs/src/conversion.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,10 @@ impl<T: Clone> CppFromUnsafe<T> for T {
3535
unsafe { (*src).clone() }
3636
}
3737
}
38+
39+
#[macro_export]
40+
macro_rules! cpp_type {
41+
($cpp:literal, $rust:ty) => {
42+
$rust
43+
};
44+
}

0 commit comments

Comments
 (0)