[clang] Add the check of membership in decltype for the issue #58674

D137531 had once fixed the issue. However, it caused a crash during compiling
llvm/unittests/IR/PatternMatch.cpp in stage-2. The reason is the predicator
isDerivedFrom does not consider independent types if the derived type is
dependent.

This patch improves D137531 by adding an option to make isDerivedFrom consider
independent types.

Differential Revision: https://reviews.llvm.org/D142437
This commit is contained in:
Liming Liu
2023-01-10 20:28:52 +08:00
parent 2667be0eb8
commit 01adf96ebc
5 changed files with 82 additions and 20 deletions

View File

@@ -57,6 +57,9 @@ Bug Fixes
- Fix crash on invalid code when looking up a destructor in a templated class
inside a namespace. This fixes
`Issue 59446 <https://github.com/llvm/llvm-project/issues/59446>`_.
- Fix an issue about ``decltype`` in the members of class templates derived from
templates with related parameters. This fixes
`Issue 58674 <https://github.com/llvm/llvm-project/issues/58674>`_.
Improvements to Clang's diagnostics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -1547,7 +1547,8 @@ public:
/// \param Base the base class we are searching for.
///
/// \returns true if this class is derived from Base, false otherwise.
bool isDerivedFrom(const CXXRecordDecl *Base) const;
bool isDerivedFrom(const CXXRecordDecl *Base,
bool LookupIndependent = false) const;
/// Determine whether this class is derived from the type \p Base.
///
@@ -1565,7 +1566,8 @@ public:
///
/// \todo add a separate parameter to configure IsDerivedFrom, rather than
/// tangling input and output in \p Paths
bool isDerivedFrom(const CXXRecordDecl *Base, CXXBasePaths &Paths) const;
bool isDerivedFrom(const CXXRecordDecl *Base, CXXBasePaths &Paths,
bool LookupIndependent = false) const;
/// Determine whether this class is virtually derived from
/// the class \p Base.

View File

@@ -64,14 +64,16 @@ void CXXBasePaths::swap(CXXBasePaths &Other) {
std::swap(DetectedVirtual, Other.DetectedVirtual);
}
bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base) const {
bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base,
bool LookupIndependent) const {
CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false,
/*DetectVirtual=*/false);
return isDerivedFrom(Base, Paths);
return isDerivedFrom(Base, Paths, LookupIndependent);
}
bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base,
CXXBasePaths &Paths) const {
CXXBasePaths &Paths,
bool LookupIndependent) const {
if (getCanonicalDecl() == Base->getCanonicalDecl())
return false;
@@ -80,9 +82,10 @@ bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base,
const CXXRecordDecl *BaseDecl = Base->getCanonicalDecl();
return lookupInBases(
[BaseDecl](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
return FindBaseClass(Specifier, Path, BaseDecl);
return Specifier->getType()->getAsRecordDecl() &&
FindBaseClass(Specifier, Path, BaseDecl);
},
Paths);
Paths, LookupIndependent);
}
bool CXXRecordDecl::isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const {

View File

@@ -2693,20 +2693,36 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
// to get this right here so that we don't end up making a
// spuriously dependent expression if we're inside a dependent
// instance method.
//
// We also don't need to do this if R resolved to a member in another
// class, which can happen in an unevaluated operand:
//
// C++ [expr.prim.id]p3.3:
// If that id-expression denotes a non-static data member and it
// appears in an unevaluated operand.
if (!R.empty() && (*R.begin())->isCXXClassMember()) {
bool MightBeImplicitMember;
if (!IsAddressOfOperand)
MightBeImplicitMember = true;
else if (!SS.isEmpty())
MightBeImplicitMember = false;
else if (R.isOverloadedResult())
MightBeImplicitMember = false;
else if (R.isUnresolvableResult())
MightBeImplicitMember = true;
else
MightBeImplicitMember = isa<FieldDecl>(R.getFoundDecl()) ||
isa<IndirectFieldDecl>(R.getFoundDecl()) ||
isa<MSPropertyDecl>(R.getFoundDecl());
bool MightBeImplicitMember = true, CheckField = true;
if (IsAddressOfOperand) {
MightBeImplicitMember = SS.isEmpty() && !R.isOverloadedResult();
CheckField = !R.isUnresolvableResult();
}
if (MightBeImplicitMember && CheckField) {
if (R.isSingleResult() &&
isa<FieldDecl, IndirectFieldDecl, MSPropertyDecl>(R.getFoundDecl())) {
auto Class = cast<CXXRecordDecl>((*R.begin())->getDeclContext());
for (auto Curr = S->getLookupEntity(); Curr && !Curr->isFileContext();
Curr = Curr->getParent()) {
if (auto ThisClass = dyn_cast_if_present<CXXRecordDecl>(Curr)) {
if ((MightBeImplicitMember =
ThisClass->Equals(Class) ||
ThisClass->isDerivedFrom(Class,
/*LookupIndependent=*/true)))
break;
}
}
} else if (IsAddressOfOperand)
MightBeImplicitMember = false;
}
if (MightBeImplicitMember)
return BuildPossibleImplicitMemberExpr(SS, TemplateKWLoc,

View File

@@ -101,6 +101,44 @@ namespace D5789 {
template<class T> void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'x' }))) {}
}
namespace GH58674 {
struct Foo {
float value_;
struct nested {
float value_;
};
};
template <typename T>
struct TemplateFoo {
float value_;
};
float bar;
template <typename T>
struct Animal{};
template <typename T>
class Cat : Animal<T> {
using okay = decltype(Foo::value_);
using also_okay = decltype(bar);
using okay2 = decltype(Foo::nested::value_);
using okay3 = decltype(TemplateFoo<T>::value_);
public:
void meow() {
using okay = decltype(Foo::value_);
using also_okay = decltype(bar);
using okay2 = decltype(Foo::nested::value_);
using okay3 = decltype(TemplateFoo<T>::value_);
}
};
void baz() {
Cat<void>{}.meow();
}
}
template<typename>
class conditional {
};