Skip to content

Commit 5865db2

Browse files
authored
Drop Init marker for rules that forward arguments (#571)
The manual Init marker on rules such as vector::emplace_back was used to encode semantic information about the rule: the forward pack is used to construct an element type. For automatically generated rules this is not a good approach. The signature does not tell how forwarded arguments are used, this information exists as text in the description of the standard, for example: ```cpp std::map<Key,T,Compare,Allocator>::emplace template< class... Args > std::pair<iterator, bool> emplace( Args&&... args ) ``` _Inserts a new element into the container constructed in-place with the given args, if there is no element with the key in the container. **The constructor of the new element (i.e. std::pair<const Key, T>) is called with exactly the same arguments as supplied to emplace, forwarded via std::forward<Args>(args)....**_ --- This PR moves the Init marker in the Rust translation: ```cpp fn f67<T1: CppLess + Clone + ByteRepr + 'static, T2: Default + Clone + ByteRepr + 'static>( a0: Ptr<Map<T1, (Value<T1>, Value<T2>)>>, // init is the pack argument which builds typename std::map<T1, T2, T3, T4>::value_type // as the text of the standard says init: cpp_type!("typename std::map<T1, T2, T3, T4>::value_type", (Value<T1>, Value<T2>)), ) -> (Value<BidirectionalIterator<(Value<T1>, Value<T2>)>>, Value<bool>) ``` cpp_type has no semantic meaning for the rule, it expands to the second argument, i.e. `(Value<T1>, Value<T2>)`. --- I also added rules for std::any which builds a `decay_t<T>` using the forwarded arguments. I wanted to make sure that this also works.
1 parent 3ac0cef commit 5865db2

30 files changed

Lines changed: 1077 additions & 62 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5284,7 +5284,7 @@ Converter::ConvertInitFragment(clang::Expr *expr,
52845284
if (!tgt_ir || !tgt_ir->usesInit()) {
52855285
llvm::report_fatal_error("expression rule does not use Init");
52865286
}
5287-
auto type = RuleRegistry::GetInitType(expr);
5287+
auto type = RuleRegistry::GetInitType(ctx_, expr);
52885288

52895289
Buffer buf(*this);
52905290
PushExprKind push(*this, ExprKind::RValue);

‎cpp2rust/converter/rules/expr_matcher.cpp‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,57 @@ void deduceMemberTypesFromOperands(
275275
}
276276
}
277277

278+
llvm::ArrayRef<clang::TemplateArgumentLoc>
279+
getExplicitTemplateArgs(const clang::Expr *expr) {
280+
auto call = llvm::dyn_cast<clang::CallExpr>(RuleDecls::SkipImplicit(expr));
281+
if (!call) {
282+
return {};
283+
}
284+
auto callee = call->getCallee()->IgnoreParenImpCasts();
285+
if (auto ref = llvm::dyn_cast<clang::DeclRefExpr>(callee)) {
286+
return ref->template_arguments();
287+
}
288+
if (auto member = llvm::dyn_cast<clang::MemberExpr>(callee)) {
289+
return member->template_arguments();
290+
}
291+
if (auto overload = llvm::dyn_cast<clang::OverloadExpr>(callee)) {
292+
return overload->template_arguments();
293+
}
294+
if (auto member =
295+
llvm::dyn_cast<clang::CXXDependentScopeMemberExpr>(callee)) {
296+
return member->template_arguments();
297+
}
298+
return {};
299+
}
300+
301+
void deduceFromExplicitArgs(clang::Sema &sema,
302+
clang::FunctionTemplateDecl *rule,
303+
const clang::Expr *use,
304+
std::vector<clang::TemplateArgument> &deduced) {
305+
auto rule_args =
306+
getExplicitTemplateArgs(RuleDecls::GetReturned(rule->getTemplatedDecl()));
307+
auto use_args = getExplicitTemplateArgs(use);
308+
for (unsigned i = 0; i < rule_args.size() && i < use_args.size(); ++i) {
309+
const auto &rule_arg = rule_args[i].getArgument();
310+
const auto &use_arg = use_args[i].getArgument();
311+
if (rule_arg.getKind() != clang::TemplateArgument::Type ||
312+
use_arg.getKind() != clang::TemplateArgument::Type) {
313+
continue;
314+
}
315+
if (rule_arg.getAsType()->getAs<clang::PackExpansionType>()) {
316+
break;
317+
}
318+
auto args = RuleDecls::DeduceFromType(
319+
sema, rule->getTemplateParameters(), rule_arg.getAsType(),
320+
use_arg.getAsType().getCanonicalType());
321+
for (unsigned j = 0; args && j < deduced.size(); ++j) {
322+
if (deduced[j].isNull()) {
323+
deduced[j] = (*args)[j];
324+
}
325+
}
326+
}
327+
}
328+
278329
std::optional<std::vector<clang::TemplateArgument>>
279330
deduceFromCallee(clang::Sema &sema, clang::FunctionTemplateDecl *rule,
280331
const clang::Expr *use) {
@@ -343,6 +394,7 @@ clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema,
343394
}
344395
}
345396
deduceMemberTypesFromOperands(sema, rule, use, deduced);
397+
deduceFromExplicitArgs(sema, rule, use, deduced);
346398
auto last = static_cast<unsigned>(deducible.find_last_unset());
347399
for (unsigned i = 0; i < params->size() && i <= last; ++i) {
348400
auto param = params->getParam(i);

‎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
clang::QualType GetDeclaredReturnType(const clang::Expr *expr) {

‎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+
}

‎libcc2rs/src/reinterpret.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ impl<T: 'static> ByteRepr for *const T {}
175175
impl<T: 'static> ByteRepr for *mut T {}
176176
impl<A: ByteRepr, B: ByteRepr> ByteRepr for (A, B) {}
177177
impl<K: 'static, V: 'static> ByteRepr for std::collections::BTreeMap<K, V> {}
178+
impl ByteRepr for std::rc::Rc<dyn std::any::Any> {}
178179

179180
// Runs `f` with a zeroed scratch buffer of `len` elements. Small buffers live
180181
// on the stack so that accessing memory through a reinterpreted pointer does

0 commit comments

Comments
 (0)