Skip to content

Commit

Permalink
Make template specialization classes upgradable.
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Goodman committed Jun 16, 2024
1 parent f017b7a commit 249ca24
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 30 deletions.
118 changes: 88 additions & 30 deletions bin/Index/Util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,8 @@ pasta::DerivedToken DerivedLocation(const pasta::DerivedToken &tok) {

namespace {

static bool RawDeclIsDefinition(const clang::Decl *decl_);

// NOTE(pag): An `alias` attribute is considered a "defining attribute".
static bool FuncIsDefinition(clang::FunctionDecl *decl) {
if (decl->isThisDeclarationADefinition()) {
Expand All @@ -563,35 +565,26 @@ static bool ParamIsDefinition(clang::ParmVarDecl *decl) {
}
}

} // namespace

// Does this look like a replaceable fragment? This happens when there's a
// method with an uninstantiated/unsubstitued body, or return type that isn't
// yet deduced.
bool IsReplaceableFragment(const std::vector<pasta::Decl> &decls) {
if (decls.size() != 1u) {
return false;
// Treat specializations of definitions as definitions. This makes sure our IDs
// are consistent in cross-references.
static bool SpecializationIsDefinition(
clang::ClassTemplateSpecializationDecl *decl) {
if (decl->isThisDeclarationADefinition()) {
return true;
}

const pasta::Decl &decl = decls.front();
switch (decl.Kind()) {
case pasta::DeclKind::kFunction:
case pasta::DeclKind::kCXXConversion:
case pasta::DeclKind::kCXXConstructor:
case pasta::DeclKind::kCXXDeductionGuide:
case pasta::DeclKind::kCXXDestructor:
case pasta::DeclKind::kCXXMethod:
break;
default:
return false;
}
auto spec_or_partial = decl->getSpecializedTemplateOrPartial();
if (auto tpl = spec_or_partial.get<clang::ClassTemplateDecl *>()) {
return RawDeclIsDefinition(tpl);

if (decl.IsImplicit()) {
return false;
} else if (auto partial = spec_or_partial.get<clang::ClassTemplatePartialSpecializationDecl *>()) {
return RawDeclIsDefinition(partial);
}

const auto &func = reinterpret_cast<const pasta::FunctionDecl &>(decl);

return false;
}

static bool IsReplaceableFunction(const pasta::FunctionDecl &func) {
// We don't expect to be looking at the pattern definition itself. We should
// really never hit this condition, because the top-level decl in `decls`
// should instead be the `FunctionTemplateDecl`.
Expand Down Expand Up @@ -624,9 +617,21 @@ bool IsReplaceableFragment(const std::vector<pasta::Decl> &decls) {
return pattern_decl->DoesThisDeclarationHaveABody();
}

// Returns `true` if `decl` is a definition.
bool IsDefinition(const pasta::Decl &decl_) {
auto decl = const_cast<clang::Decl *>(decl_.RawDecl()->RemappedDecl);
static bool IsReplaceableClass(
const pasta::ClassTemplateSpecializationDecl &spec) {
return IsDefinition(spec) != spec.IsThisDeclarationADefinition();
}

static bool TemplateIsDefinition(clang::RedeclarableTemplateDecl *decl) {
if (auto from_tpl = decl->getInstantiatedFromMemberTemplate()) {
return RawDeclIsDefinition(from_tpl);
}

return false;
}

static bool RawDeclIsDefinition(const clang::Decl *decl_) {
auto decl = decl_->RemappedDecl;

if (auto parm_decl = clang::dyn_cast<clang::ParmVarDecl>(decl)) {
return ParamIsDefinition(parm_decl);
Expand All @@ -653,17 +658,32 @@ bool IsDefinition(const pasta::Decl &decl_) {
} else if (auto face_decl = clang::dyn_cast<clang::ObjCInterfaceDecl>(decl)) {
return face_decl->isThisDeclarationADefinition();

} else if (auto spec = clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(decl)) {
return SpecializationIsDefinition(spec);

} else if (auto tag_decl = clang::dyn_cast<clang::TagDecl>(decl)) {
return tag_decl->isThisDeclarationADefinition();

} else if (auto ctpl_decl = clang::dyn_cast<clang::ClassTemplateDecl>(decl)) {
return ctpl_decl->isThisDeclarationADefinition();
if (ctpl_decl->isThisDeclarationADefinition()) {
return true;
}

return TemplateIsDefinition(ctpl_decl);

} else if (auto ftpl_decl = clang::dyn_cast<clang::FunctionTemplateDecl>(decl)) {
return ftpl_decl->isThisDeclarationADefinition();
if (ftpl_decl->isThisDeclarationADefinition()) {
return true;
}

return TemplateIsDefinition(ctpl_decl);

} else if (auto vtpl_decl = clang::dyn_cast<clang::VarTemplateDecl>(decl)) {
return vtpl_decl->isThisDeclarationADefinition();
if (vtpl_decl->isThisDeclarationADefinition()) {
return true;
}

return TemplateIsDefinition(ctpl_decl);

} else if (clang::isa<clang::EnumConstantDecl>(decl)) {
return true;
Expand All @@ -673,6 +693,44 @@ bool IsDefinition(const pasta::Decl &decl_) {
}
}

} // namespace

// Does this look like a replaceable fragment? This happens when there's a
// method with an uninstantiated/unsubstitued body, or return type that isn't
// yet deduced, or class template specialization where the pattern is a
// defintion but the specialization isn't.
bool IsReplaceableFragment(const std::vector<pasta::Decl> &decls) {
if (decls.size() != 1u) {
return false;
}

const pasta::Decl &decl = decls.front();
if (decl.IsImplicit()) {
return false;
}

switch (decl.Kind()) {
case pasta::DeclKind::kFunction:
case pasta::DeclKind::kCXXConversion:
case pasta::DeclKind::kCXXConstructor:
case pasta::DeclKind::kCXXDeductionGuide:
case pasta::DeclKind::kCXXDestructor:
case pasta::DeclKind::kCXXMethod:
return IsReplaceableFunction(
reinterpret_cast<const pasta::FunctionDecl &>(decl));
case pasta::DeclKind::kClassTemplateSpecialization:
return IsReplaceableClass(
reinterpret_cast<const pasta::ClassTemplateSpecializationDecl &>(decl));
default:
return false;
}
}

// Returns `true` if `decl` is a definition.
bool IsDefinition(const pasta::Decl &decl) {
return RawDeclIsDefinition(decl.RawDecl());
}

// Checks if the declaration is valid and serializable
bool IsSerializableDecl(const pasta::Decl &decl) {
switch (decl.Kind()) {
Expand Down
11 changes: 11 additions & 0 deletions tests/Cxx/UpgradeSpecialization.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "UpgradeSpecialization.h"

namespace UpgradeSpecialization {

#ifdef UPGRADE_SPEC
auto x_val = TemplateClass<int>().Method();
#else
TemplateClass<int> *x_ptr;
#endif

} // namespace UpgradeSpecialization
8 changes: 8 additions & 0 deletions tests/Cxx/UpgradeSpecialization.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace UpgradeSpecialization {

template <typename T>
class TemplateClass {
T Method(void);
};

} // namespace UpgradeSpecialization
10 changes: 10 additions & 0 deletions tests/Cxx/compile_commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,5 +213,15 @@
"file": "LambdaInConstructorInClassInFunction.cpp",
"command": "/usr/bin/c++ -std=c++20 LambdaInConstructorInClassInFunction.cpp",
"directory": ""
},
{
"file": "UpgradeSpecialization.cpp",
"command": "/usr/bin/c++ -std=c++20 UpgradeSpecialization.cpp",
"directory": ""
},
{
"file": "UpgradeSpecialization.cpp",
"command": "/usr/bin/c++ -std=c++20 UpgradeSpecialization.cpp -DUPGRADE_SPEC=1",
"directory": ""
}
]

0 comments on commit 249ca24

Please sign in to comment.