Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cpp2rust/converter/mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ clang::PrintingPolicy getPrintPolicy() {
policy.SuppressScope = false;
policy.FullyQualifiedName = true;
policy.SuppressUnwrittenScope = true;
policy.UsePreferredNames = true;
return policy;
}

Expand Down
160 changes: 152 additions & 8 deletions cpp2rust/cpp_rule_preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Distributed under the MIT license that can be found in the LICENSE file.

#include <clang/AST/ASTContext.h>
#include <clang/AST/Attr.h>
#include <clang/AST/Decl.h>
#include <clang/AST/Expr.h>
#include <clang/AST/PrettyPrinter.h>
Expand Down Expand Up @@ -58,10 +59,15 @@ struct LookupInfo {
name = dm->getMember();
kind = LookupKind::CXXMethodName;
explicitArgs = dm->template_arguments();
} else if (const auto *um =
llvm::dyn_cast<clang::UnresolvedMemberExpr>(expr)) {
name = um->getMemberName();
kind = LookupKind::CXXMethodName;
explicitArgs = um->template_arguments();
} else if (const auto *dref =
llvm::dyn_cast<clang::DependentScopeDeclRefExpr>(expr)) {
clang::DeclarationName dname = dref->getDeclName();
if (name.getNameKind() ==
if (dname.getNameKind() ==
clang::DeclarationName::NameKind::CXXConstructorName) {
name = dname;
kind = LookupKind::CXXConstructorName;
Expand Down Expand Up @@ -260,7 +266,8 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
return spec;
}

if (result == clang::TemplateDeductionResult::SubstitutionFailure) {
if (result == clang::TemplateDeductionResult::SubstitutionFailure ||
result == clang::TemplateDeductionResult::ConstraintsNotSatisfied) {
if (const auto *deduced = info.takeCanonical()) {
clang::TemplateArgumentListInfo targsInfo;
for (const auto &arg : deduced->asArray()) {
Expand Down Expand Up @@ -292,7 +299,9 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
return ns;
}

clang::RecordDecl *createRecordDecl(llvm::StringRef name) {
clang::RecordDecl *
createRecordDecl(llvm::StringRef name,
clang::QualType base = clang::QualType()) {
bool owned = true;
bool dependent = false;
clang::CXXScopeSpec scope;
Expand All @@ -305,11 +314,135 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
clang::TypeResult(), false, false, clang::OffsetOfKind::Outside);
assert(decl.isUsable() && "Record decl creation failed");
auto *rdecl = decl.getAs<clang::RecordDecl>();

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<clang::CXXRecordDecl>(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 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<clang::ClassTemplateSpecializationDecl>(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);
}

auto *pattern = clang::CXXRecordDecl::Create(
ctx, clang::TagTypeKind::Struct, sema_->CurContext, loc_, loc_,
&ctx.Idents.get(name));

auto *htdecl = hspec->getSpecializedTemplate();
auto *mirror = clang::ClassTemplateDecl::Create(
ctx, sema_->CurContext, loc_,
clang::DeclarationName(&ctx.Idents.get(name)),
htdecl->getTemplateParameters(), pattern);
pattern->setDescribedClassTemplate(mirror);

clang::TemplateArgumentListInfo bargs(loc_, loc_);
for (const clang::TemplateArgument &arg :
htdecl->getTemplateParameters()->getInjectedTemplateArgs(ctx)) {
bargs.addArgument(
sema_->getTrivialTemplateArgumentLoc(arg, getNTTPType(arg), loc_));
}
clang::QualType base_t = sema_->CheckTemplateIdType(
clang::ElaboratedTypeKeyword::None, clang::TemplateName(htdecl), loc_,
bargs, sema_->getCurScope(), /*ForNestedNameSpecifier=*/false);
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);
pattern->completeDefinition();
sema_->CurContext->addDecl(mirror);

clang::TemplateArgumentListInfo args(loc_, loc_);
for (const clang::TemplateArgument &arg :
hspec->getTemplateArgs().asArray()) {
args.addArgument(
sema_->getTrivialTemplateArgumentLoc(arg, getNTTPType(arg), loc_));
}

clang::QualType spec = sema_->CheckTemplateIdType(
clang::ElaboratedTypeKeyword::None, clang::TemplateName(mirror), loc_,
args, sema_->getCurScope(), /*ForNestedNameSpecifier=*/false);
assert(!spec.isNull() && spec->getAsCXXRecordDecl());

// required to print Tn instead of Tn<arg1, arg2, ...>
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
getDefaultArg(clang::TemplateDecl *decl,
const clang::TemplateTypeParmDecl *parm,
llvm::ArrayRef<clang::TemplateArgument> currentArgs) {
clang::QualType type = parm->getDefaultArgument().getArgument().getAsType();
if (!type->isDependentType()) {
return type;
}

clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl);
assert(!Inst.isInvalid() && "Invalid instantiation context");

clang::MultiLevelTemplateArgumentList mtal;
mtal.setKind(clang::TemplateSubstitutionKind::Rewrite);
mtal.addOuterTemplateArguments(currentArgs);

clang::TypeSourceInfo *tsi =
sema_->SubstType(sema_->Context.getTrivialTypeSourceInfo(type), mtal,
loc_, clang::DeclarationName());
assert(tsi && "Template argument type instantiation failed");
return tsi->getType();
}

clang::VarDecl *createVarDecl(clang::QualType type, llvm::StringRef name,
clang::StorageClass sclass = clang::SC_None) {
clang::ASTContext &ctx = sema_->Context;
Expand Down Expand Up @@ -354,11 +487,19 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
createTemplateArguments(clang::TemplateDecl *decl,
llvm::SmallVectorImpl<clang::TemplateArgument> &out) {
for (clang::NamedDecl *param : *decl->getTemplateParameters()) {
if (llvm::isa<clang::TemplateTypeParmDecl>(param)) {
clang::RecordDecl *rdecl = createRecordDecl(param->getName());
clang::QualType type =
sema_->Context.getTagType(clang::ElaboratedTypeKeyword::None,
rdecl->getQualifier(), rdecl, false);
if (const auto *ttp =
llvm::dyn_cast<clang::TemplateTypeParmDecl>(param)) {
clang::QualType type;
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<clang::NonTypeTemplateParmDecl>(param)) {
Expand Down Expand Up @@ -504,6 +645,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
clang::NamespaceDecl *ns = createNamespaceDecl();
clang::Sema::ContextRAII savedContext(*sema_, ns);
clang::FunctionDecl *rule = instantiateRuleDecl(decl);
assert(rule && "Rule instantiation failed");
llvm::ArrayRef<clang::ParmVarDecl *> parms = rule->parameters();
auto csk = lookup.name.getNameKind() ==
clang::DeclarationName::NameKind::CXXOperatorName
Expand Down Expand Up @@ -610,6 +752,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
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");
Expand All @@ -627,6 +770,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
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());
Expand Down
Loading