Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -293,6 +293,9 @@ and Clang 15 accidentally stopped predeclaring those functions in that language mode. Clang 16 now predeclares those functions again. This fixes `Issue 56607 `_. +- Fix an issue about ``decltype`` in the members of class templates derived from + templates with related parameters. + `Issue 58674 `_ Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -781,8 +784,8 @@ - Introduced the new function ``clang_CXXMethod_isCopyAssignmentOperator``, which identifies whether a method cursor is a copy-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 @@ -2693,19 +2693,28 @@ // spuriously dependent expression if we're inside a dependent // instance method. 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())) { + if (auto Class = dyn_cast_if_present( + (*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 { };