Skip to content

Commit

Permalink
Reapply "[Clang][Sema] Refactor collection of multi-level template ar…
Browse files Browse the repository at this point in the history
…gument lists (llvm#106585, llvm#111173)" (llvm#111852)

This patch reapplies llvm#111173, fixing a bug when instantiating dependent
expressions that name a member template that is later explicitly
specialized for a class specialization that is implicitly instantiated.

The bug is addressed by adding the `hasMemberSpecialization` function,
which return `true` if _any_ redeclaration is a member specialization.
This is then used when determining the instantiation pattern for a
specialization of a template, and when collecting template arguments for
a specialization of a template.
  • Loading branch information
sdkrystian authored Oct 11, 2024
1 parent 48bda00 commit 2bb3d3a
Show file tree
Hide file tree
Showing 21 changed files with 1,066 additions and 704 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ Bug Fixes to C++ Support
in certain friend declarations. (#GH93099)
- Clang now instantiates the correct lambda call operator when a lambda's class type is
merged across modules. (#GH110401)
- Clang now uses the correct set of template argument lists when comparing the constraints of
out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of
a class template. (#GH102320)
- Fix a crash when parsing a pseudo destructor involving an invalid type. (#GH111460)
- Fixed an assertion failure when invoking recovery call expressions with explicit attributes
and undeclared templates. (#GH107047, #GH49093)
Expand Down
97 changes: 59 additions & 38 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -781,15 +781,11 @@ class RedeclarableTemplateDecl : public TemplateDecl,
EntryType *Entry, void *InsertPos);

struct CommonBase {
CommonBase() : InstantiatedFromMember(nullptr, false) {}
CommonBase() {}

/// The template from which this was most
/// directly instantiated (or null).
///
/// The boolean value indicates whether this template
/// was explicitly specialized.
llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool>
InstantiatedFromMember;
RedeclarableTemplateDecl *InstantiatedFromMember = nullptr;

/// If non-null, points to an array of specializations (including
/// partial specializations) known only by their external declaration IDs.
Expand All @@ -809,14 +805,19 @@ class RedeclarableTemplateDecl : public TemplateDecl,
};

/// Pointer to the common data shared by all declarations of this
/// template.
mutable CommonBase *Common = nullptr;
/// template, and a flag indicating if the template is a member
/// specialization.
mutable llvm::PointerIntPair<CommonBase *, 1, bool> Common;

CommonBase *getCommonPtrInternal() const { return Common.getPointer(); }

/// Retrieves the "common" pointer shared by all (re-)declarations of
/// the same template. Calling this routine may implicitly allocate memory
/// for the common pointer.
CommonBase *getCommonPtr() const;

void setCommonPtr(CommonBase *C) const { Common.setPointer(C); }

virtual CommonBase *newCommon(ASTContext &C) const = 0;

// Construct a template decl with name, parameters, and templated element.
Expand Down Expand Up @@ -857,15 +858,22 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// template<> template<typename T>
/// struct X<int>::Inner { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
return getCommonPtr()->InstantiatedFromMember.getInt();
bool isMemberSpecialization() const { return Common.getInt(); }

/// Determines whether any redeclaration of this template was
/// a specialization of a member template.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (D->isMemberSpecialization())
return true;
}
return false;
}

/// Note that this member template is a specialization.
void setMemberSpecialization() {
assert(getCommonPtr()->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
getCommonPtr()->InstantiatedFromMember.setInt(true);
assert(!isMemberSpecialization() && "already a member specialization");
Common.setInt(true);
}

/// Retrieve the member template from which this template was
Expand Down Expand Up @@ -905,12 +913,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
/// void X<T>::f(T, U);
/// \endcode
RedeclarableTemplateDecl *getInstantiatedFromMemberTemplate() const {
return getCommonPtr()->InstantiatedFromMember.getPointer();
return getCommonPtr()->InstantiatedFromMember;
}

void setInstantiatedFromMemberTemplate(RedeclarableTemplateDecl *TD) {
assert(!getCommonPtr()->InstantiatedFromMember.getPointer());
getCommonPtr()->InstantiatedFromMember.setPointer(TD);
assert(!getCommonPtr()->InstantiatedFromMember);
getCommonPtr()->InstantiatedFromMember = TD;
}

/// Retrieve the "injected" template arguments that correspond to the
Expand Down Expand Up @@ -1989,6 +1997,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// template arguments have been deduced.
void setInstantiationOf(ClassTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Already set to a class template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
Expand All @@ -2000,6 +2010,8 @@ class ClassTemplateSpecializationDecl : public CXXRecordDecl,
/// Note that this class template specialization is an instantiation
/// of the given class template.
void setInstantiationOf(ClassTemplateDecl *TemplDecl) {
assert(!isa<ClassTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization*>() &&
"Previously set to a class template partial specialization!");
SpecializedTemplate = TemplDecl;
Expand Down Expand Up @@ -2187,19 +2199,23 @@ class ClassTemplatePartialSpecializationDecl
/// struct X<int>::Inner<T*> { /* ... */ };
/// \endcode
bool isMemberSpecialization() const {
const auto *First =
cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
return First->InstantiatedFromMember.getInt();
return InstantiatedFromMember.getInt();
}

/// Note that this member template is a specialization.
void setMemberSpecialization() {
auto *First = cast<ClassTemplatePartialSpecializationDecl>(getFirstDecl());
assert(First->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
return First->InstantiatedFromMember.setInt(true);
/// Determines whether any redeclaration of this this class template partial
/// specialization was a specialization of a member partial specialization.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (cast<ClassTemplatePartialSpecializationDecl>(D)
->isMemberSpecialization())
return true;
}
return false;
}

/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }

/// Retrieves the injected specialization type for this partial
/// specialization. This is not the same as the type-decl-type for
/// this partial specialization, which is an InjectedClassNameType.
Expand Down Expand Up @@ -2268,10 +2284,6 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
return static_cast<Common *>(RedeclarableTemplateDecl::getCommonPtr());
}

void setCommonPtr(Common *C) {
RedeclarableTemplateDecl::Common = C;
}

public:

friend class ASTDeclReader;
Expand Down Expand Up @@ -2754,6 +2766,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// template arguments have been deduced.
void setInstantiationOf(VarTemplatePartialSpecializationDecl *PartialSpec,
const TemplateArgumentList *TemplateArgs) {
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Already set to a variable template partial specialization!");
auto *PS = new (getASTContext()) SpecializedPartialSpecialization();
Expand All @@ -2765,6 +2779,8 @@ class VarTemplateSpecializationDecl : public VarDecl,
/// Note that this variable template specialization is an instantiation
/// of the given variable template.
void setInstantiationOf(VarTemplateDecl *TemplDecl) {
assert(!isa<VarTemplatePartialSpecializationDecl>(this) &&
"A partial specialization cannot be instantiated from a template");
assert(!SpecializedTemplate.is<SpecializedPartialSpecialization *>() &&
"Previously set to a variable template partial specialization!");
SpecializedTemplate = TemplDecl;
Expand Down Expand Up @@ -2949,19 +2965,24 @@ class VarTemplatePartialSpecializationDecl
/// U* X<int>::Inner<T*> = (T*)(0) + 1;
/// \endcode
bool isMemberSpecialization() const {
const auto *First =
cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
return First->InstantiatedFromMember.getInt();
return InstantiatedFromMember.getInt();
}

/// Note that this member template is a specialization.
void setMemberSpecialization() {
auto *First = cast<VarTemplatePartialSpecializationDecl>(getFirstDecl());
assert(First->InstantiatedFromMember.getPointer() &&
"Only member templates can be member template specializations");
return First->InstantiatedFromMember.setInt(true);
/// Determines whether any redeclaration of this this variable template
/// partial specialization was a specialization of a member partial
/// specialization.
bool hasMemberSpecialization() const {
for (const auto *D : redecls()) {
if (cast<VarTemplatePartialSpecializationDecl>(D)
->isMemberSpecialization())
return true;
}
return false;
}

/// Note that this member template is a specialization.
void setMemberSpecialization() { return InstantiatedFromMember.setInt(true); }

SourceRange getSourceRange() const override LLVM_READONLY;

void Profile(llvm::FoldingSetNodeID &ID) const {
Expand Down
25 changes: 6 additions & 19 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11327,9 +11327,9 @@ class Sema final : public SemaBase {
CXXScopeSpec &SS, IdentifierInfo *Name, SourceLocation NameLoc,
const ParsedAttributesView &Attr, TemplateParameterList *TemplateParams,
AccessSpecifier AS, SourceLocation ModulePrivateLoc,
SourceLocation FriendLoc, unsigned NumOuterTemplateParamLists,
TemplateParameterList **OuterTemplateParamLists,
SkipBodyInfo *SkipBody = nullptr);
SourceLocation FriendLoc,
ArrayRef<TemplateParameterList *> OuterTemplateParamLists,
bool IsMemberSpecialization, SkipBodyInfo *SkipBody = nullptr);

/// Translates template arguments as provided by the parser
/// into template arguments used by semantic analysis.
Expand Down Expand Up @@ -11368,7 +11368,8 @@ class Sema final : public SemaBase {
DeclResult ActOnVarTemplateSpecialization(
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
StorageClass SC, bool IsPartialSpecialization);
StorageClass SC, bool IsPartialSpecialization,
bool IsMemberSpecialization);

/// Get the specialization of the given variable template corresponding to
/// the specified argument list, or a null-but-valid result if the arguments
Expand Down Expand Up @@ -13009,28 +13010,14 @@ class Sema final : public SemaBase {
/// dealing with a specialization. This is only relevant for function
/// template specializations.
///
/// \param Pattern If non-NULL, indicates the pattern from which we will be
/// instantiating the definition of the given declaration, \p ND. This is
/// used to determine the proper set of template instantiation arguments for
/// friend function template specializations.
///
/// \param ForConstraintInstantiation when collecting arguments,
/// ForConstraintInstantiation indicates we should continue looking when
/// encountering a lambda generic call operator, and continue looking for
/// arguments on an enclosing class template.
///
/// \param SkipForSpecialization when specified, any template specializations
/// in a traversal would be ignored.
/// \param ForDefaultArgumentSubstitution indicates we should continue looking
/// when encountering a specialized member function template, rather than
/// returning immediately.
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
const NamedDecl *D, const DeclContext *DC = nullptr, bool Final = false,
std::optional<ArrayRef<TemplateArgument>> Innermost = std::nullopt,
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
bool ForConstraintInstantiation = false,
bool SkipForSpecialization = false,
bool ForDefaultArgumentSubstitution = false);
bool RelativeToPrimary = false, bool ForConstraintInstantiation = false);

/// RAII object to handle the state changes required to synthesize
/// a function body.
Expand Down
37 changes: 18 additions & 19 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2704,21 +2704,21 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {
if (isTemplateInstantiation(VDTemplSpec->getTemplateSpecializationKind())) {
auto From = VDTemplSpec->getInstantiatedFrom();
if (auto *VTD = From.dyn_cast<VarTemplateDecl *>()) {
while (!VTD->isMemberSpecialization()) {
auto *NewVTD = VTD->getInstantiatedFromMemberTemplate();
if (!NewVTD)
while (!VTD->hasMemberSpecialization()) {
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
break;
VTD = NewVTD;
}
return getDefinitionOrSelf(VTD->getTemplatedDecl());
}
if (auto *VTPSD =
From.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
while (!VTPSD->isMemberSpecialization()) {
auto *NewVTPSD = VTPSD->getInstantiatedFromMember();
if (!NewVTPSD)
while (!VTPSD->hasMemberSpecialization()) {
if (auto *NewVTPSD = VTPSD->getInstantiatedFromMember())
VTPSD = NewVTPSD;
else
break;
VTPSD = NewVTPSD;
}
return getDefinitionOrSelf<VarDecl>(VTPSD);
}
Expand All @@ -2727,15 +2727,14 @@ VarDecl *VarDecl::getTemplateInstantiationPattern() const {

// If this is the pattern of a variable template, find where it was
// instantiated from. FIXME: Is this necessary?
if (VarTemplateDecl *VarTemplate = VD->getDescribedVarTemplate()) {
while (!VarTemplate->isMemberSpecialization()) {
auto *NewVT = VarTemplate->getInstantiatedFromMemberTemplate();
if (!NewVT)
if (VarTemplateDecl *VTD = VD->getDescribedVarTemplate()) {
while (!VTD->hasMemberSpecialization()) {
if (auto *NewVTD = VTD->getInstantiatedFromMemberTemplate())
VTD = NewVTD;
else
break;
VarTemplate = NewVT;
}

return getDefinitionOrSelf(VarTemplate->getTemplatedDecl());
return getDefinitionOrSelf(VTD->getTemplatedDecl());
}

if (VD == this)
Expand Down Expand Up @@ -4150,11 +4149,11 @@ FunctionDecl::getTemplateInstantiationPattern(bool ForDefinition) const {
if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) {
// If we hit a point where the user provided a specialization of this
// template, we're done looking.
while (!ForDefinition || !Primary->isMemberSpecialization()) {
auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate();
if (!NewPrimary)
while (!ForDefinition || !Primary->hasMemberSpecialization()) {
if (auto *NewPrimary = Primary->getInstantiatedFromMemberTemplate())
Primary = NewPrimary;
else
break;
Primary = NewPrimary;
}

return getDefinitionOrSelf(Primary->getTemplatedDecl());
Expand Down
14 changes: 8 additions & 6 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2023,19 +2023,21 @@ const CXXRecordDecl *CXXRecordDecl::getTemplateInstantiationPattern() const {
if (auto *TD = dyn_cast<ClassTemplateSpecializationDecl>(this)) {
auto From = TD->getInstantiatedFrom();
if (auto *CTD = From.dyn_cast<ClassTemplateDecl *>()) {
while (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate()) {
if (NewCTD->isMemberSpecialization())
while (!CTD->hasMemberSpecialization()) {
if (auto *NewCTD = CTD->getInstantiatedFromMemberTemplate())
CTD = NewCTD;
else
break;
CTD = NewCTD;
}
return GetDefinitionOrSelf(CTD->getTemplatedDecl());
}
if (auto *CTPSD =
From.dyn_cast<ClassTemplatePartialSpecializationDecl *>()) {
while (auto *NewCTPSD = CTPSD->getInstantiatedFromMember()) {
if (NewCTPSD->isMemberSpecialization())
while (!CTPSD->hasMemberSpecialization()) {
if (auto *NewCTPSD = CTPSD->getInstantiatedFromMemberTemplate())
CTPSD = NewCTPSD;
else
break;
CTPSD = NewCTPSD;
}
return GetDefinitionOrSelf(CTPSD);
}
Expand Down
Loading

0 comments on commit 2bb3d3a

Please sign in to comment.