Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -304,6 +304,9 @@ - GNU attributes being applied prior to standard attributes would be handled improperly, which was corrected to match the behaviour exhibited by GCC. `Issue 58229 `_ +- Fix an issue about ``decltype`` in the members of class templates derived from + templates with related parameters. + `Issue 58674 `_ Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -815,8 +818,8 @@ - Introduced the new function ``clang_CXXMethod_isMoveAssignmentOperator``, which identifies whether a method cursor is a move-assignment operator. -- ``clang_Cursor_getNumTemplateArguments``, ``clang_Cursor_getTemplateArgumentKind``, - ``clang_Cursor_getTemplateArgumentType``, ``clang_Cursor_getTemplateArgumentValue`` and +- ``clang_Cursor_getNumTemplateArguments``, ``clang_Cursor_getTemplateArgumentKind``, + ``clang_Cursor_getTemplateArgumentType``, ``clang_Cursor_getTemplateArgumentValue`` and ``clang_Cursor_getTemplateArgumentUnsignedValue`` now work on struct, class, and partial template specialization cursors in addition to function cursors. Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -2692,20 +2692,34 @@ // 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))) + break; + } + } + } else if (IsAddressOfOperand) + MightBeImplicitMember = false; + } if (MightBeImplicitMember) return BuildPossibleImplicitMemberExpr(SS, TemplateKWLoc, 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 { };