Skip to content

Commit

Permalink
[clang] Add the check of membership for the issue #58674 and improve …
Browse files Browse the repository at this point in the history
…the lookup process

This patch includes the commit 01adf96 and a fix of unhandled declaration
references.

When looking up base classes, Clang first checks whether a base class is a
template and takes the specialized template based on it. However, the base class
might be instantiated, and the above behavior can lose information.

This patch fixes the problem by first checking whether a base class is a record
declaration, so the instantiated one will be taken.

Differential Revision: https://reviews.llvm.org/D143840
  • Loading branch information
limingliv committed Feb 22, 2023
1 parent d3e8495 commit 8498ba6
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 30 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -165,6 +165,9 @@ Bug Fixes to C++ Support
- Fix crash when evaluating consteval constructor of derived class whose base
has more than one field.
(`#60166 <https://github.com/llvm/llvm-project/issues/60166>`_)
- Fix an issue about ``decltype`` in the members of class templates derived from
templates with related parameters.
(`#58674 <https://github.com/llvm/llvm-project/issues/58674>`_)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
10 changes: 8 additions & 2 deletions clang/include/clang/AST/DeclCXX.h
Expand Up @@ -1546,8 +1546,11 @@ class CXXRecordDecl : public RecordDecl {
///
/// \param Base the base class we are searching for.
///
/// \param LookupInDependentTypes whether to look up in dependent types.
///
/// \returns true if this class is derived from Base, false otherwise.
bool isDerivedFrom(const CXXRecordDecl *Base) const;
bool isDerivedFrom(const CXXRecordDecl *Base,
bool LookupInDependentTypes = false) const;

/// Determine whether this class is derived from the type \p Base.
///
Expand All @@ -1561,11 +1564,14 @@ class CXXRecordDecl : public RecordDecl {
/// \param Paths will contain the paths taken from the current class to the
/// given \p Base class.
///
/// \param LookupInDependentTypes whether to look up independent types.
///
/// \returns true if this class is derived from \p Base, false otherwise.
///
/// \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 LookupInDependentTypes = false) const;

/// Determine whether this class is virtually derived from
/// the class \p Base.
Expand Down
31 changes: 16 additions & 15 deletions clang/lib/AST/CXXInheritance.cpp
Expand Up @@ -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 LookupInDependentTypes) const {
CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false,
/*DetectVirtual=*/false);
return isDerivedFrom(Base, Paths);
return isDerivedFrom(Base, Paths, LookupInDependentTypes);
}

bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base,
CXXBasePaths &Paths) const {
CXXBasePaths &Paths,
bool LookupInDependentTypes) const {
if (getCanonicalDecl() == Base->getCanonicalDecl())
return false;

Expand All @@ -83,7 +85,7 @@ bool CXXRecordDecl::isDerivedFrom(const CXXRecordDecl *Base,
return Specifier->getType()->getAsRecordDecl() &&
FindBaseClass(Specifier, Path, BaseDecl);
},
Paths);
Paths, LookupInDependentTypes);
}

bool CXXRecordDecl::isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const {
Expand Down Expand Up @@ -246,17 +248,16 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context,
} else if (VisitBase) {
CXXRecordDecl *BaseRecord;
if (LookupInDependent) {
BaseRecord = nullptr;
const TemplateSpecializationType *TST =
BaseSpec.getType()->getAs<TemplateSpecializationType>();
if (!TST) {
if (auto *RT = BaseSpec.getType()->getAs<RecordType>())
BaseRecord = cast<CXXRecordDecl>(RT->getDecl());
} else {
TemplateName TN = TST->getTemplateName();
if (auto *TD =
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()))
BaseRecord = TD->getTemplatedDecl();
BaseRecord = cast_if_present<CXXRecordDecl>(
BaseSpec.getType()->getAsRecordDecl());
if (!BaseRecord) {
if (const TemplateSpecializationType *TST =
BaseSpec.getType()->getAs<TemplateSpecializationType>()) {
TemplateName TN = TST->getTemplateName();
if (auto *TD =
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()))
BaseRecord = TD->getTemplatedDecl();
}
}
if (BaseRecord) {
if (!BaseRecord->hasDefinition() ||
Expand Down
42 changes: 29 additions & 13 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -2698,20 +2698,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,
Expand Down
22 changes: 22 additions & 0 deletions clang/test/CodeGenCXX/decl-ref-inheritance.cpp
@@ -0,0 +1,22 @@
// RUN: %clang_cc1 -triple=x86_64-unknown-linux -emit-llvm %s -o - | FileCheck %s

// CHECK: [[FOO:%.+]] = type { i32 }
struct foo {
int val;
};

template <typename T> struct bar : T {
};

struct baz : bar<foo> {
// CHECK-LABEL: define{{.*}} i32 @_ZN3baz3getEv
// CHECK: {{%.+}} = getelementptr inbounds [[FOO]], ptr {{%.+}}, i32 0, i32 0
int get() {
return val;
}
};

int qux() {
auto f = baz{};
return f.get();
}
38 changes: 38 additions & 0 deletions clang/test/SemaCXX/decltype.cpp
Expand Up @@ -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 {
};
Expand Down

0 comments on commit 8498ba6

Please sign in to comment.