[clang] [clang] Look through alias templates when visiting dependent base classes (PR #223713)
Christian Kandeler via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 16 03:34:17 PDT 2026
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 <christian.kandeler at qt.io>
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 <christian.kandeler at qt.io>
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 <christian.kandeler at qt.io>
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 <christian.kandeler at qt.io>
Date: Wed, 16 Sep 2026 12:34:06 +0200
Subject: [PATCH 4/4] Update clang/lib/Sema/SemaLookup.cpp
Co-authored-by: Younan Zhang <zyn7109 at gmail.com>
---
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();
More information about the cfe-commits
mailing list