Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -67,6 +67,9 @@ - Fix crash when evaluating consteval constructor of derived class whose base has more than one field. This fixes `Issue 60166 `_. +- Fix an issue about ``decltype`` in the members of class templates derived from + templates with related parameters. This fixes + `Issue 58674 `_. Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -1546,8 +1546,11 @@ /// /// \param Base the base class we are searching for. /// + /// \param LookupIndependent whether look up independent types. + /// /// \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. /// @@ -1561,11 +1564,14 @@ /// \param Paths will contain the paths taken from the current class to the /// given \p Base class. /// + /// \param LookupIndependent whether 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 LookupIndependent = false) const; /// Determine whether this class is virtually derived from /// the class \p Base. Index: clang/lib/AST/CXXInheritance.cpp =================================================================== --- clang/lib/AST/CXXInheritance.cpp +++ clang/lib/AST/CXXInheritance.cpp @@ -63,15 +63,16 @@ std::swap(DetectVirtual, Other.DetectVirtual); 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; @@ -83,7 +84,7 @@ return Specifier->getType()->getAsRecordDecl() && FindBaseClass(Specifier, Path, BaseDecl); }, - Paths); + Paths, LookupIndependent); } bool CXXRecordDecl::isVirtuallyDerivedFrom(const CXXRecordDecl *Base) const { @@ -246,17 +247,16 @@ } else if (VisitBase) { CXXRecordDecl *BaseRecord; if (LookupInDependent) { - BaseRecord = nullptr; - const TemplateSpecializationType *TST = - BaseSpec.getType()->getAs(); - if (!TST) { - if (auto *RT = BaseSpec.getType()->getAs()) - BaseRecord = cast(RT->getDecl()); - } else { - TemplateName TN = TST->getTemplateName(); - if (auto *TD = - dyn_cast_or_null(TN.getAsTemplateDecl())) - BaseRecord = TD->getTemplatedDecl(); + BaseRecord = cast_if_present( + BaseSpec.getType()->getAsRecordDecl()); + if (!BaseRecord) { + if (const TemplateSpecializationType *TST = + BaseSpec.getType()->getAs()) { + TemplateName TN = TST->getTemplateName(); + if (auto *TD = + dyn_cast_or_null(TN.getAsTemplateDecl())) + BaseRecord = TD->getTemplatedDecl(); + } } if (BaseRecord) { if (!BaseRecord->hasDefinition() || Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -2693,20 +2693,36 @@ // 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(R.getFoundDecl()) || - isa(R.getFoundDecl()) || - isa(R.getFoundDecl()); + bool MightBeImplicitMember = true, CheckField = true; + if (IsAddressOfOperand) { + MightBeImplicitMember = SS.isEmpty() && !R.isOverloadedResult(); + CheckField = !R.isUnresolvableResult(); + } + if (MightBeImplicitMember && CheckField) { + if (R.isSingleResult() && + isa(R.getFoundDecl())) { + auto Class = cast((*R.begin())->getDeclContext()); + for (auto Curr = S->getLookupEntity(); Curr && !Curr->isFileContext(); + Curr = Curr->getParent()) { + if (auto ThisClass = dyn_cast_if_present(Curr)) { + if ((MightBeImplicitMember = + ThisClass->Equals(Class) || + ThisClass->isDerivedFrom(Class, + /*LookupIndependent=*/true))) + break; + } + } + } else if (IsAddressOfOperand) + MightBeImplicitMember = false; + } if (MightBeImplicitMember) return BuildPossibleImplicitMemberExpr(SS, TemplateKWLoc, Index: clang/test/CodeGenCXX/decl-ref-inheritance.cpp =================================================================== --- /dev/null +++ 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 struct bar : T { +}; + +struct baz : bar { + // 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(); +} Index: clang/test/SemaCXX/decltype.cpp =================================================================== --- clang/test/SemaCXX/decltype.cpp +++ clang/test/SemaCXX/decltype.cpp @@ -101,6 +101,44 @@ template void foo(decltype(T(LP1{ .p1 = g1, .p1.x[1] = 'x' }))) {} } +namespace GH58674 { + struct Foo { + float value_; + struct nested { + float value_; + }; + }; + + template + struct TemplateFoo { + float value_; + }; + + float bar; + + template + struct Animal{}; + + template + class Cat : Animal { + using okay = decltype(Foo::value_); + using also_okay = decltype(bar); + using okay2 = decltype(Foo::nested::value_); + using okay3 = decltype(TemplateFoo::value_); + public: + void meow() { + using okay = decltype(Foo::value_); + using also_okay = decltype(bar); + using okay2 = decltype(Foo::nested::value_); + using okay3 = decltype(TemplateFoo::value_); + } + }; + + void baz() { + Cat{}.meow(); + } +} + template class conditional { };