Author: Christian Kandeler
Date: 2026-09-17T13:18:43+02:00
New Revision: 743442442b21040955cd76ba47ff9f3657132c31

URL: 
https://github.com/llvm/llvm-project/commit/743442442b21040955cd76ba47ff9f3657132c31
DIFF: 
https://github.com/llvm/llvm-project/commit/743442442b21040955cd76ba47ff9f3657132c31.diff

LOG: [clang] Look through alias templates when visiting dependent base classes 
(#223713)

When gathering the visible declarations of a class 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`. The base is then skipped, along with everything
it declares.

libstdc++'s `std::allocator` runs into this: it derives from
`__allocator_base<T>`, which is an alias template for
`__new_allocator<T>`. Before C++20, `allocate()` and `deallocate()` are
declared only in that base (the declarations in `allocator` itself are
guarded by `__cpp_constexpr_dynamic_alloc`), so a dependent
`std::allocator<T>` offered no members at all beyond the implicitly
declared ones:

```c++
#include <memory>
template <class T> void f(std::allocator<T> a) {
  a. // nothing useful was offered here
}
```

Look through the alias to the class template it ultimately names.

Assisted-by: Claude Opus 5

---------

Co-authored-by: Younan Zhang <[email protected]>
Co-authored-by: Corentin Jabot <[email protected]>

Added: 
    clang/test/CodeCompletion/dependent-base-alias-template.cpp

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaLookup.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 89df8e80459f0..dfc03787678f9 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -882,6 +882,9 @@ 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 suggested by code completion when relevant.
+
 ### Static Analyzer
 
 #### Crash and bug fixes

diff  --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 78365323477c3..bc8a30d4e9dad 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -4262,7 +4262,10 @@ 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.
+          const auto *TST = 
BaseType->getAsNonAliasTemplateSpecializationType();
           if (!TST)
             continue;
           TemplateName TN = TST->getTemplateName();

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()


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

Reply via email to