https://github.com/ckandeler updated https://github.com/llvm/llvm-project/pull/223713
>From 5c2a8baf6abb3e4870d8587d1943b60a19f4e400 Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Tue, 15 Sep 2026 14:05:29 +0200 Subject: [PATCH 1/6] [clang] Look through alias templates when visiting dependent base classes When gathering visible declarations for code completion, a dependent base class is resolved by taking its TemplateSpecializationType and casting the template it names to a ClassTemplateDecl. If the base is named through an alias template, that cast fails, because the TemplateName refers to a TypeAliasTemplateDecl, and the base is skipped along with everything it declares. libstdc++'s std::allocator<T> hits this: it derives from __allocator_base<T>, an alias template for __new_allocator<T>. In C++17, where allocate() and deallocate() are declared only in that base, a dependent `std::allocator<T>` therefore offered no members at all beyond the implicitly declared ones. Look through the alias to the class template it ultimately names. Assisted-by: Claude Opus 5 --- clang/lib/Sema/SemaLookup.cpp | 13 +++++++ .../dependent-base-alias-template.cpp | 35 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 clang/test/CodeCompletion/dependent-base-alias-template.cpp diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 78365323477c38..0f4811b612a607 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4265,6 +4265,19 @@ class LookupVisibleHelper { const auto *TST = BaseType->getAs<TemplateSpecializationType>(); if (!TST) continue; + // The base may be named through an alias template, whose + // TemplateName is a TypeAliasTemplateDecl rather than the + // ClassTemplateDecl we are after. Look through the alias, otherwise + // the base is skipped entirely. libstdc++'s std::allocator<T> hits + // this: it derives from __allocator_base<T>, an alias template for + // __new_allocator<T>. + while (TST->isTypeAlias()) { + const auto *Aliased = + TST->getAliasedType()->getAs<TemplateSpecializationType>(); + if (!Aliased) + break; + TST = Aliased; + } TemplateName TN = TST->getTemplateName(); const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()); diff --git a/clang/test/CodeCompletion/dependent-base-alias-template.cpp b/clang/test/CodeCompletion/dependent-base-alias-template.cpp new file mode 100644 index 00000000000000..7aef777245def6 --- /dev/null +++ b/clang/test/CodeCompletion/dependent-base-alias-template.cpp @@ -0,0 +1,35 @@ +template <typename T> struct Base { + void fromBase(); +}; + +// A dependent base class can be named through an alias template. libstdc++'s +// std::allocator does exactly this: it derives from __allocator_base<T>, which +// is an alias template for __new_allocator<T>. +template <typename T> using AliasBase = Base<T>; +template <typename T> using AliasOfAlias = AliasBase<T>; + +template <typename T> struct Derived : AliasBase<T> { + void ownMember(); +}; + +template <typename T> struct DerivedTwice : AliasOfAlias<T> { + void ownMember(); +}; + +template <typename T> void f(Derived<T> d) { + d. +} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++17 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: COMPLETION: Base (InBase) : Base:: +// CHECK-CC1: COMPLETION: Derived : Derived:: +// CHECK-CC1: COMPLETION: fromBase (InBase) : [#void#][#Base<T>::#]fromBase() +// CHECK-CC1: COMPLETION: ownMember : [#void#]ownMember() + +template <typename T> void g(DerivedTwice<T> d) { + d. +} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-2):5 -std=c++17 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: COMPLETION: Base (InBase) : Base:: +// CHECK-CC2: COMPLETION: DerivedTwice : DerivedTwice:: +// CHECK-CC2: COMPLETION: fromBase (InBase) : [#void#][#Base<T>::#]fromBase() +// CHECK-CC2: COMPLETION: ownMember : [#void#]ownMember() >From 7cb0179a12b702c609ede40dbd91516a35b65fe8 Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Wed, 16 Sep 2026 10:55:28 +0200 Subject: [PATCH 2/6] [clang] Add a release note Assisted-by: Claude Opus 5 --- clang/docs/ReleaseNotes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 18d2895531ea60..e43775e389c66a 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -879,6 +879,11 @@ features cannot lower the translation-unit ABI level; `set_x(decltype(x) val)`. This affects the completion strings produced by libclang as well as those used by clangd. +- Members inherited from a dependent base class that is named through an alias + template are now offered. Previously such a base was skipped entirely, so for + example a dependent `std::allocator<T>`, which derives from the alias template + `__allocator_base<T>`, offered none of the members it inherits. + ### Static Analyzer #### Crash and bug fixes >From e655573d0c844003a4bd6daec8d544401816e577 Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Wed, 16 Sep 2026 11:49:39 +0200 Subject: [PATCH 3/6] [clang] Address review: use getAsNonAliasTemplateSpecializationType() Replace the hand-rolled loop with the existing helper, which does the same thing and documents this as its intended use. Assisted-by: Claude Opus 5 --- clang/lib/Sema/SemaLookup.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0f4811b612a607..3f6ba28c399326 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4262,22 +4262,14 @@ class LookupVisibleHelper { // there anyway. continue; } - const auto *TST = BaseType->getAs<TemplateSpecializationType>(); + // Look through an alias template, whose TemplateName is a + // TypeAliasTemplateDecl rather than the ClassTemplateDecl we are + // after; otherwise the base is skipped entirely. libstdc++'s + // std::allocator<T> relies on this: it derives from + // __allocator_base<T>, an alias template for __new_allocator<T>. + const auto *TST = BaseType->getAsNonAliasTemplateSpecializationType(); if (!TST) continue; - // The base may be named through an alias template, whose - // TemplateName is a TypeAliasTemplateDecl rather than the - // ClassTemplateDecl we are after. Look through the alias, otherwise - // the base is skipped entirely. libstdc++'s std::allocator<T> hits - // this: it derives from __allocator_base<T>, an alias template for - // __new_allocator<T>. - while (TST->isTypeAlias()) { - const auto *Aliased = - TST->getAliasedType()->getAs<TemplateSpecializationType>(); - if (!Aliased) - break; - TST = Aliased; - } TemplateName TN = TST->getTemplateName(); const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl()); >From a4587170bece4aedc412554443cdd8b23180887d Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Wed, 16 Sep 2026 12:34:06 +0200 Subject: [PATCH 4/6] Update clang/lib/Sema/SemaLookup.cpp Co-authored-by: Younan Zhang <[email protected]> --- clang/lib/Sema/SemaLookup.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 3f6ba28c399326..1c95c0f2179499 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4264,10 +4264,7 @@ class LookupVisibleHelper { } // Look through an alias template, whose TemplateName is a // TypeAliasTemplateDecl rather than the ClassTemplateDecl we are - // after; otherwise the base is skipped entirely. libstdc++'s - // std::allocator<T> relies on this: it derives from - // __allocator_base<T>, an alias template for __new_allocator<T>. - const auto *TST = BaseType->getAsNonAliasTemplateSpecializationType(); + // after; otherwise the base is skipped entirely. if (!TST) continue; TemplateName TN = TST->getTemplateName(); >From 956b32f507a9c9eb5b94d5de492beabbb43926af Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Wed, 16 Sep 2026 13:08:40 +0200 Subject: [PATCH 5/6] [clang] Restore a declaration dropped by the previous commit Applying the review suggestion through the GitHub UI replaced the line below the comment as well, removing the declaration of TST. Assisted-by: Claude Opus 5 --- clang/lib/Sema/SemaLookup.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 1c95c0f2179499..bc8a30d4e9dad4 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4265,6 +4265,7 @@ class LookupVisibleHelper { // Look through an alias template, whose TemplateName is a // TypeAliasTemplateDecl rather than the ClassTemplateDecl we are // after; otherwise the base is skipped entirely. + const auto *TST = BaseType->getAsNonAliasTemplateSpecializationType(); if (!TST) continue; TemplateName TN = TST->getTemplateName(); >From 599b47be071011cf9f2fb024cb41181ac594b995 Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Wed, 16 Sep 2026 15:23:19 +0200 Subject: [PATCH 6/6] Update clang/docs/ReleaseNotes.md Co-authored-by: Corentin Jabot <[email protected]> --- clang/docs/ReleaseNotes.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index e43775e389c66a..7816d1e3c283db 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -880,9 +880,7 @@ features cannot lower the translation-unit ABI level; libclang as well as those used by clangd. - Members inherited from a dependent base class that is named through an alias - template are now offered. Previously such a base was skipped entirely, so for - example a dependent `std::allocator<T>`, which derives from the alias template - `__allocator_base<T>`, offered none of the members it inherits. + template are suggested by code completion when relevant. ### Static Analyzer _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
