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/5] [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/5] [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/5] [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/5] 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(); >From 8e5e3583b441b1b6efa4a6078f2571a6db813b37 Mon Sep 17 00:00:00 2001 From: Christian Kandeler <[email protected]> Date: Wed, 16 Sep 2026 13:08:40 +0200 Subject: [PATCH 5/5] [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 1c95c0f217949..bc8a30d4e9dad 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(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
