https://github.com/ckandeler updated 
https://github.com/llvm/llvm-project/pull/223713

>From 1ebc3ea2aa9743c590b6533cd3917d2c213d80b7 Mon Sep 17 00:00:00 2001
From: Christian Kandeler <[email protected]>
Date: Tue, 15 Sep 2026 14:05:29 +0200
Subject: [PATCH 1/4] [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 78365323477c3..0f4811b612a60 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 0000000000000..7aef777245def
--- /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 94ac2852b1fa218ea0d86a767e19c2bf5fc8fb83 Mon Sep 17 00:00:00 2001
From: Christian Kandeler <[email protected]>
Date: Wed, 16 Sep 2026 10:55:28 +0200
Subject: [PATCH 2/4] [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 d9ac67d1a2824..780d08bdccd30 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -793,6 +793,11 @@ features cannot lower the translation-unit ABI level;
 
 ### Code Completion
 
+- 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 36bf30342de38b430c5120090ca6c7b05c9b144e Mon Sep 17 00:00:00 2001
From: Christian Kandeler <[email protected]>
Date: Wed, 16 Sep 2026 11:49:39 +0200
Subject: [PATCH 3/4] [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 0f4811b612a60..3f6ba28c39932 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 e6aa3cba70d49080aa36f3cda17ef16dde811c74 Mon Sep 17 00:00:00 2001
From: Christian Kandeler <[email protected]>
Date: Wed, 16 Sep 2026 12:34:06 +0200
Subject: [PATCH 4/4] 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 3f6ba28c39932..1c95c0f217949 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();

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to