Skip to content

Commit d9271e1

Browse files
authored
Extend cpp-rule-preprocessor supported C++ rules (#187)
Extends the set of rules that `cpp-rule-preprocessor` can handle. The biggest additions are `createMirrorType` and support for adding type hints to IR rules in the form of default template arguments. One limitation on the kinds of rules that could be processed was that the synthesized PODs did not necessarily exhibit all of the characteristics required to instantiate a given C++ construct. This patch adds support for specifying default template arguments in rules, which are used as a reference for the characteristics that the synthesized structs must exhibit. This information is then used by `createMirrorType`. When the provided type is a non-template class, the synthesized struct simply inherits from it. When the provided type is a class template, the synthesized struct becomes an instantiation of a synthesized class template that inherits from it. Roughly: ```cpp // rule with hint template <typename T1, typename T2 = std::allocator<T1>> void f1() { (...) } // what is roughly synthesized: // POD, same as before struct T1 {}; // synthesized class template that inherits from the hint template <typename _Tp> class Mirror : public std::allocator<_Tp> {}; // instantiation of the synthesized class template template <> class Mirror<T1> {}; ``` Using a synthesized class template rather than inheriting directly allows the synthesized struct to match the hinted type structurally. This is useful when instantiating constructs that perform structural checks, such as: ```cpp // bits/alloc_traits.h static_assert(is_same< typename __replace_first_arg<_Tp, typename _Tp::value_type>::type, _Tp>::value, "allocator_traits<A>::rebind_alloc<A::value_type> must be A"); ``` These changes enable `cpp-rule-preprocessor` to parse 479 automatically generated rule sources for `std::vector` and its specializations.
1 parent 609ae54 commit d9271e1

9 files changed

Lines changed: 1658 additions & 13 deletions

File tree

cpp2rust/converter/mapper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ clang::PrintingPolicy getPrintPolicy() {
4040
policy.SuppressScope = false;
4141
policy.FullyQualifiedName = true;
4242
policy.SuppressUnwrittenScope = true;
43+
policy.UsePreferredNames = true;
4344
return policy;
4445
}
4546

cpp2rust/cpp_rule_preprocessor.cpp

Lines changed: 200 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Distributed under the MIT license that can be found in the LICENSE file.
33

44
#include <clang/AST/ASTContext.h>
5+
#include <clang/AST/Attr.h>
56
#include <clang/AST/Decl.h>
67
#include <clang/AST/Expr.h>
78
#include <clang/AST/PrettyPrinter.h>
@@ -58,10 +59,15 @@ struct LookupInfo {
5859
name = dm->getMember();
5960
kind = LookupKind::CXXMethodName;
6061
explicitArgs = dm->template_arguments();
62+
} else if (const auto *um =
63+
llvm::dyn_cast<clang::UnresolvedMemberExpr>(expr)) {
64+
name = um->getMemberName();
65+
kind = LookupKind::CXXMethodName;
66+
explicitArgs = um->template_arguments();
6167
} else if (const auto *dref =
6268
llvm::dyn_cast<clang::DependentScopeDeclRefExpr>(expr)) {
6369
clang::DeclarationName dname = dref->getDeclName();
64-
if (name.getNameKind() ==
70+
if (dname.getNameKind() ==
6571
clang::DeclarationName::NameKind::CXXConstructorName) {
6672
name = dname;
6773
kind = LookupKind::CXXConstructorName;
@@ -260,7 +266,8 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
260266
return spec;
261267
}
262268

263-
if (result == clang::TemplateDeductionResult::SubstitutionFailure) {
269+
if (result == clang::TemplateDeductionResult::SubstitutionFailure ||
270+
result == clang::TemplateDeductionResult::ConstraintsNotSatisfied) {
264271
if (const auto *deduced = info.takeCanonical()) {
265272
clang::TemplateArgumentListInfo targsInfo;
266273
for (const auto &arg : deduced->asArray()) {
@@ -293,7 +300,9 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
293300
return ns;
294301
}
295302

296-
clang::RecordDecl *createRecordDecl(llvm::StringRef name) {
303+
clang::RecordDecl *
304+
createRecordDecl(llvm::StringRef name,
305+
clang::QualType base = clang::QualType()) {
297306
bool owned = true;
298307
bool dependent = false;
299308
clang::CXXScopeSpec scope;
@@ -306,11 +315,183 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
306315
clang::TypeResult(), false, false, clang::OffsetOfKind::Outside);
307316
assert(decl.isUsable() && "Record decl creation failed");
308317
auto *rdecl = decl.getAs<clang::RecordDecl>();
318+
309319
rdecl->startDefinition();
320+
if (!base.isNull()) {
321+
clang::CXXBaseSpecifier baseSpec(
322+
clang::SourceRange(loc_, loc_), false, true, clang::AS_public,
323+
sema_->Context.getTrivialTypeSourceInfo(base, loc_),
324+
/*EllipsisLoc=*/clang::SourceLocation());
325+
const clang::CXXBaseSpecifier *bases[] = {&baseSpec};
326+
llvm::cast<clang::CXXRecordDecl>(rdecl)->setBases(bases, 1);
327+
}
310328
rdecl->completeDefinition();
311329
return rdecl;
312330
}
313331

332+
static clang::QualType getNTTPType(const clang::TemplateArgument &arg) {
333+
switch (arg.getKind()) {
334+
case clang::TemplateArgument::Integral:
335+
return arg.getIntegralType();
336+
case clang::TemplateArgument::Declaration:
337+
return arg.getParamTypeForDecl();
338+
case clang::TemplateArgument::NullPtr:
339+
return arg.getNullPtrType();
340+
case clang::TemplateArgument::StructuralValue:
341+
return arg.getStructuralValueType();
342+
default:
343+
return clang::QualType();
344+
}
345+
}
346+
347+
clang::QualType
348+
getTemplateIdType(clang::ClassTemplateDecl *decl,
349+
llvm::ArrayRef<clang::TemplateArgument> args) {
350+
clang::TemplateArgumentListInfo info(loc_, loc_);
351+
for (const clang::TemplateArgument &arg : args) {
352+
info.addArgument(
353+
sema_->getTrivialTemplateArgumentLoc(arg, getNTTPType(arg), loc_));
354+
}
355+
return sema_->CheckTemplateIdType(clang::ElaboratedTypeKeyword::None,
356+
clang::TemplateName(decl), loc_, info,
357+
sema_->getCurScope(),
358+
/*ForNestedNameSpecifier=*/false);
359+
}
360+
361+
using MirrorMap = llvm::SmallDenseMap<const clang::ClassTemplateDecl *,
362+
clang::ClassTemplateDecl *>;
363+
364+
clang::TypeSourceInfo *findMatch(MirrorMap &substs, clang::QualType type) {
365+
const auto *tst = type->getAs<clang::TemplateSpecializationType>();
366+
if (!tst) {
367+
return nullptr;
368+
}
369+
370+
const auto *tdecl = llvm::dyn_cast_or_null<clang::ClassTemplateDecl>(
371+
tst->getTemplateName().getAsTemplateDecl());
372+
if (!tdecl) {
373+
return nullptr;
374+
}
375+
376+
if (auto it = substs.find(tdecl->getCanonicalDecl()); it != substs.end()) {
377+
auto match = getTemplateIdType(it->second, tst->template_arguments());
378+
assert(!match.isNull());
379+
return sema_->Context.getTrivialTypeSourceInfo(match, loc_);
380+
}
381+
return nullptr;
382+
}
383+
384+
clang::ClassTemplateDecl *
385+
createInheritingTemplate(llvm::StringRef name, clang::ClassTemplateDecl *decl,
386+
MirrorMap &substs) {
387+
clang::ASTContext &ctx = sema_->Context;
388+
auto *pattern = clang::CXXRecordDecl::Create(
389+
ctx, clang::TagTypeKind::Struct, sema_->CurContext, loc_, loc_,
390+
&ctx.Idents.get(name));
391+
392+
auto *mirror = clang::ClassTemplateDecl::Create(
393+
ctx, sema_->CurContext, loc_,
394+
clang::DeclarationName(&ctx.Idents.get(name)),
395+
decl->getTemplateParameters(), pattern);
396+
pattern->setDescribedClassTemplate(mirror);
397+
mirror->setAccess(clang::AS_public);
398+
substs.try_emplace(decl->getCanonicalDecl(), mirror);
399+
400+
clang::QualType base_t = getTemplateIdType(
401+
decl, decl->getTemplateParameters()->getInjectedTemplateArgs(ctx));
402+
assert(!base_t.isNull() && "Failed building mirror base");
403+
404+
pattern->startDefinition();
405+
clang::CXXBaseSpecifier base(clang::SourceRange(loc_, loc_), false, true,
406+
clang::AS_public,
407+
ctx.getTrivialTypeSourceInfo(base_t, loc_),
408+
/*EllipsisLoc=*/clang::SourceLocation());
409+
const clang::CXXBaseSpecifier *bases[] = {&base};
410+
pattern->setBases(bases, 1);
411+
412+
for (auto *member : decl->getTemplatedDecl()->decls()) {
413+
if (const auto *td = llvm::dyn_cast<clang::TypedefNameDecl>(member)) {
414+
if (auto *replacement = findMatch(substs, td->getUnderlyingType())) {
415+
auto *copy = clang::TypedefDecl::Create(
416+
ctx, pattern, loc_, loc_, td->getIdentifier(), replacement);
417+
copy->setAccess(clang::AS_public);
418+
pattern->addDecl(copy);
419+
}
420+
} else if (auto *tdecl =
421+
llvm::dyn_cast<clang::ClassTemplateDecl>(member)) {
422+
clang::Sema::ContextRAII savedContext(*sema_, pattern);
423+
createInheritingTemplate(tdecl->getName(), tdecl, substs);
424+
}
425+
}
426+
427+
pattern->completeDefinition();
428+
sema_->CurContext->addDecl(mirror);
429+
return mirror;
430+
}
431+
432+
clang::QualType createMirrorType(llvm::StringRef name, clang::QualType hint) {
433+
clang::ASTContext &ctx = sema_->Context;
434+
forceCompleteDefinition(hint);
435+
436+
const auto *hdecl = hint->getAsCXXRecordDecl();
437+
assert(hdecl && "Failed resolving hint record declaration");
438+
assert(hdecl->isCompleteDefinition() && "Incomplete hint");
439+
440+
const auto *hspec =
441+
llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(hdecl);
442+
if (!hspec) {
443+
// if it is not a template specialization inheriting from it suffices
444+
clang::RecordDecl *rdecl = createRecordDecl(name, hint);
445+
return ctx.getTagType(clang::ElaboratedTypeKeyword::None,
446+
rdecl->getQualifier(), rdecl, false);
447+
}
448+
449+
MirrorMap substs;
450+
auto *mirror =
451+
createInheritingTemplate(name, hspec->getSpecializedTemplate(), substs);
452+
clang::QualType spec =
453+
getTemplateIdType(mirror, hspec->getTemplateArgs().asArray());
454+
assert(!spec.isNull() && spec->getAsCXXRecordDecl());
455+
456+
// required to print Tn instead of Tn<arg1, arg2, ...>
457+
clang::NamespaceDecl *ns = createNamespaceDecl();
458+
auto *alias =
459+
clang::TypeAliasDecl::Create(ctx, ns, loc_, loc_, &ctx.Idents.get(name),
460+
ctx.getTrivialTypeSourceInfo(spec, loc_));
461+
ns->addDecl(alias);
462+
463+
clang::QualType alias_t = ctx.getTypedefType(
464+
clang::ElaboratedTypeKeyword::None, std::nullopt, alias);
465+
spec->getAsCXXRecordDecl()->addAttr(
466+
clang::PreferredNameAttr::CreateImplicit(
467+
ctx, ctx.getTrivialTypeSourceInfo(alias_t, loc_)));
468+
469+
return ctx.getCanonicalType(spec);
470+
}
471+
472+
clang::QualType
473+
getDefaultArg(clang::TemplateDecl *decl,
474+
const clang::TemplateTypeParmDecl *parm,
475+
llvm::ArrayRef<clang::TemplateArgument> currentArgs) {
476+
clang::QualType type = parm->getDefaultArgument().getArgument().getAsType();
477+
if (!type->isDependentType()) {
478+
return type;
479+
}
480+
481+
clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl);
482+
assert(!Inst.isInvalid() && "Invalid instantiation context");
483+
484+
clang::MultiLevelTemplateArgumentList mtal;
485+
mtal.setKind(clang::TemplateSubstitutionKind::Rewrite);
486+
mtal.addOuterTemplateArguments(currentArgs);
487+
488+
clang::TypeSourceInfo *tsi =
489+
sema_->SubstType(sema_->Context.getTrivialTypeSourceInfo(type), mtal,
490+
loc_, clang::DeclarationName());
491+
assert(tsi && "Template argument type instantiation failed");
492+
return tsi->getType();
493+
}
494+
314495
clang::VarDecl *createVarDecl(clang::QualType type, llvm::StringRef name,
315496
clang::StorageClass sclass = clang::SC_None) {
316497
clang::ASTContext &ctx = sema_->Context;
@@ -355,11 +536,19 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
355536
createTemplateArguments(clang::TemplateDecl *decl,
356537
llvm::SmallVectorImpl<clang::TemplateArgument> &out) {
357538
for (clang::NamedDecl *param : *decl->getTemplateParameters()) {
358-
if (llvm::isa<clang::TemplateTypeParmDecl>(param)) {
359-
clang::RecordDecl *rdecl = createRecordDecl(param->getName());
360-
clang::QualType type =
361-
sema_->Context.getTagType(clang::ElaboratedTypeKeyword::None,
362-
rdecl->getQualifier(), rdecl, false);
539+
if (const auto *ttp =
540+
llvm::dyn_cast<clang::TemplateTypeParmDecl>(param)) {
541+
clang::QualType type;
542+
if (ttp->hasDefaultArgument()) {
543+
clang::QualType hint = getDefaultArg(decl, ttp, out);
544+
assert(!hint.isNull() && "Failed retrieving type hint");
545+
type = createMirrorType(param->getName(), hint);
546+
} else {
547+
clang::RecordDecl *rdecl = createRecordDecl(param->getName());
548+
type = sema_->Context.getTagType(clang::ElaboratedTypeKeyword::None,
549+
rdecl->getQualifier(), rdecl, false);
550+
}
551+
assert(!type.isNull() && "Template type argument creation failed");
363552
out.emplace_back(type);
364553
} else if (const auto *nttp =
365554
llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(param)) {
@@ -505,6 +694,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
505694
clang::NamespaceDecl *ns = createNamespaceDecl();
506695
clang::Sema::ContextRAII savedContext(*sema_, ns);
507696
clang::FunctionDecl *rule = instantiateRuleDecl(decl);
697+
assert(rule && "Rule instantiation failed");
508698
llvm::ArrayRef<clang::ParmVarDecl *> parms = rule->parameters();
509699
auto csk = lookup.name.getNameKind() ==
510700
clang::DeclarationName::NameKind::CXXOperatorName
@@ -611,6 +801,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
611801
clang::NamespaceDecl *ns = createNamespaceDecl();
612802
clang::Sema::ContextRAII savedContext(*sema_, ns);
613803
clang::FunctionDecl *rule = instantiateRuleDecl(decl);
804+
assert(rule && "Rule instantiation failed");
614805
clang::CXXRecordDecl *rdecl =
615806
resolveCXXRecordDecl(rule->getParamDecl(0)->getType());
616807
assert(rdecl && "Failed fetching record decl");
@@ -628,6 +819,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
628819
clang::NamespaceDecl *ns = createNamespaceDecl();
629820
clang::Sema::ContextRAII savedContext(*sema_, ns);
630821
clang::FunctionDecl *rule = instantiateRuleDecl(decl);
822+
assert(rule && "Rule instantiation failed");
631823

632824
clang::Expr *obj = createOpaqueValueExpr(
633825
rule->getParamDecl(0)->getType().getNonReferenceType());

rule-preprocessor/src/syntactic.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ impl SyntacticAnalysis {
121121
let mut file_ir = FileIr::new();
122122

123123
for fn_item in source_file.syntax().descendants().filter_map(ast::Fn::cast) {
124+
if !cfg_matches_host(&fn_item) {
125+
continue;
126+
}
127+
124128
let Some(name) = fn_item.name() else { continue };
125129
let fn_name = name.text().to_string();
126130

@@ -130,9 +134,6 @@ impl SyntacticAnalysis {
130134
RuleIr::Type(TypeIrBuilder::new(&fn_item).build()),
131135
);
132136
} else if fn_name.starts_with('f') {
133-
if !cfg_matches_host(&fn_item) {
134-
continue;
135-
}
136137
file_ir.insert(
137138
fn_name.clone(),
138139
RuleIr::Fn(FnIrBuilder::new(&fn_item).build(path)),

0 commit comments

Comments
 (0)