[clang] [Clang] Substitute for the type aliases inside of a CTAD guide (PR #94740)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 29 02:47:43 PDT 2024


https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/94740

>From 2f60e51f2017e4448047f64983b2f22cdb67e816 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 7 Jun 2024 18:08:10 +0800
Subject: [PATCH 1/5] [Clang] Substitute for the type aliases inside of a CTAD
 guide

---
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/lib/Sema/SemaTemplate.cpp               | 96 +++++++++++++++++--
 .../SemaTemplate/nested-deduction-guides.cpp  | 70 ++++++++++++++
 3 files changed, 160 insertions(+), 7 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0c700d23257bf..3f6d040b0ddf1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -823,6 +823,7 @@ Bug Fixes to C++ Support
   differering by their constraints when only one of these function was variadic.
 - Fix a crash when a variable is captured by a block nested inside a lambda. (Fixes #GH93625).
 - Fixed a type constraint substitution issue involving a generic lambda expression. (#GH93821)
+- Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614).
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 40a759ea330de..1e921dd26bd7d 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2220,23 +2220,101 @@ namespace {
 class ExtractTypeForDeductionGuide
   : public TreeTransform<ExtractTypeForDeductionGuide> {
   llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs;
+  ClassTemplateDecl *NestedPattern;
+  const MultiLevelTemplateArgumentList *OuterInstantiationArgs;
 
 public:
   typedef TreeTransform<ExtractTypeForDeductionGuide> Base;
   ExtractTypeForDeductionGuide(
       Sema &SemaRef,
-      llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs)
-      : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs) {}
+      llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
+      ClassTemplateDecl *NestedPattern,
+      const MultiLevelTemplateArgumentList *OuterInstantiationArgs)
+      : Base(SemaRef), MaterializedTypedefs(MaterializedTypedefs),
+        NestedPattern(NestedPattern),
+        OuterInstantiationArgs(OuterInstantiationArgs) {}
 
   TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
 
+  bool mightReferToOuterTemplateParameters(TypedefNameDecl *Typedef) {
+    if (!NestedPattern)
+      return false;
+
+    static auto WalkUp = [](DeclContext *DC, DeclContext *TargetDC) {
+      if (DC == TargetDC)
+        return true;
+      while (!DC->isTranslationUnit()) {
+        if (DC->Equals(TargetDC))
+          return true;
+        DC = DC->getParent();
+      }
+      return false;
+    };
+
+    if (WalkUp(Typedef->getDeclContext(), NestedPattern->getTemplatedDecl()))
+      return true;
+    if (WalkUp(NestedPattern->getTemplatedDecl(), Typedef->getDeclContext()))
+      return true;
+    return false;
+  }
+
+  QualType
+  RebuildTemplateSpecializationType(TemplateName Template,
+                                    SourceLocation TemplateNameLoc,
+                                    TemplateArgumentListInfo &TemplateArgs) {
+    if (!OuterInstantiationArgs ||
+        !isa_and_present<TypeAliasTemplateDecl>(Template.getAsTemplateDecl()))
+      return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
+                                                     TemplateArgs);
+
+    auto *TATD = cast<TypeAliasTemplateDecl>(Template.getAsTemplateDecl());
+    auto *Pattern = TATD;
+    while (Pattern->getInstantiatedFromMemberTemplate())
+      Pattern = Pattern->getInstantiatedFromMemberTemplate();
+    if (!mightReferToOuterTemplateParameters(Pattern->getTemplatedDecl()))
+      return Base::RebuildTemplateSpecializationType(Template, TemplateNameLoc,
+                                                     TemplateArgs);
+
+    Decl *NewD = SemaRef.SubstDecl(
+        TATD, SemaRef.getASTContext().getTranslationUnitDecl(),
+        *OuterInstantiationArgs);
+    if (!NewD)
+      return QualType();
+
+    auto *NewTATD = cast<TypeAliasTemplateDecl>(NewD);
+    MaterializedTypedefs.push_back(NewTATD->getTemplatedDecl());
+
+    return Base::RebuildTemplateSpecializationType(
+        TemplateName(NewTATD), TemplateNameLoc, TemplateArgs);
+  }
+
   QualType TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) {
     ASTContext &Context = SemaRef.getASTContext();
     TypedefNameDecl *OrigDecl = TL.getTypedefNameDecl();
     TypedefNameDecl *Decl = OrigDecl;
     // Transform the underlying type of the typedef and clone the Decl only if
     // the typedef has a dependent context.
-    if (OrigDecl->getDeclContext()->isDependentContext()) {
+    bool InDependentContext = OrigDecl->getDeclContext()->isDependentContext();
+
+    // A typedef/alias Decl within the NestedPattern may reference the outer
+    // template parameters. They're substituted with corresponding instantiation
+    // arguments here and in RebuildTemplateSpecializationType() above.
+    // Otherwise, we would have a CTAD guide with "dangling" template
+    // parameters.
+    // For example,
+    //   template <class T> struct Outer {
+    //     using Alias = S<T>;
+    //     template <class U> struct Inner {
+    //       Inner(Alias);
+    //     };
+    //   };
+    if (OuterInstantiationArgs && InDependentContext) {
+      Decl = cast_if_present<TypedefNameDecl>(SemaRef.SubstDecl(
+          OrigDecl, Context.getTranslationUnitDecl(), *OuterInstantiationArgs));
+      if (!Decl)
+        return QualType();
+      MaterializedTypedefs.push_back(Decl);
+    } else if (InDependentContext) {
       TypeLocBuilder InnerTLB;
       QualType Transformed =
           TransformType(InnerTLB, OrigDecl->getTypeSourceInfo()->getTypeLoc());
@@ -2577,8 +2655,9 @@ struct ConvertConstructorToDeductionGuideTransform {
       // defined outside of the surrounding class template. That is T in the
       // above example.
       if (NestedPattern) {
-        NewParam = transformFunctionTypeParam(NewParam, OuterInstantiationArgs,
-                                              MaterializedTypedefs);
+        NewParam = transformFunctionTypeParam(
+            NewParam, OuterInstantiationArgs, MaterializedTypedefs,
+            /*TransformingOuterPatterns=*/true);
         if (!NewParam)
           return QualType();
       }
@@ -2630,7 +2709,8 @@ struct ConvertConstructorToDeductionGuideTransform {
 
   ParmVarDecl *transformFunctionTypeParam(
       ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args,
-      llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs) {
+      llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
+      bool TransformingOuterPatterns = false) {
     TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
     TypeSourceInfo *NewDI;
     if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {
@@ -2653,7 +2733,9 @@ struct ConvertConstructorToDeductionGuideTransform {
     // members of the current instantiations with the definitions of those
     // typedefs, avoiding triggering instantiation of the deduced type during
     // deduction.
-    NewDI = ExtractTypeForDeductionGuide(SemaRef, MaterializedTypedefs)
+    NewDI = ExtractTypeForDeductionGuide(
+                SemaRef, MaterializedTypedefs, NestedPattern,
+                TransformingOuterPatterns ? &Args : nullptr)
                 .transform(NewDI);
 
     // Resolving a wording defect, we also inherit default arguments from the
diff --git a/clang/test/SemaTemplate/nested-deduction-guides.cpp b/clang/test/SemaTemplate/nested-deduction-guides.cpp
index 38410b93ead3b..ff432173200c7 100644
--- a/clang/test/SemaTemplate/nested-deduction-guides.cpp
+++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp
@@ -16,3 +16,73 @@ using T = A<void>::B<int>;
 
 using Copy = decltype(copy);
 using Copy = A<void>::B<int>;
+
+namespace GH94614 {
+
+template <class, class> struct S {};
+
+struct trouble_1 {
+} constexpr t1;
+struct trouble_2 {
+} constexpr t2;
+struct trouble_3 {
+} constexpr t3;
+struct trouble_4 {
+} constexpr t4;
+struct trouble_5 {
+} constexpr t5;
+struct trouble_6 {
+} constexpr t6;
+struct trouble_7 {
+} constexpr t7;
+struct trouble_8 {
+} constexpr t8;
+struct trouble_9 {
+} constexpr t9;
+
+template <class U, class... T> struct Unrelated {
+  using Trouble = S<U, T...>;
+
+  template <class... V> using Trouble2 = S<V..., T...>;
+};
+
+template <class T, class U> struct Outer {
+  using Trouble = S<U, T>;
+
+  template <class V> using Trouble2 = S<V, T>;
+
+  template <class V> using Trouble3 = S<U, T>;
+
+  template <class V> struct Inner {
+    template <class W> struct Paranoid {
+      using Trouble4 = S<W, T>;
+
+      template <class... X> using Trouble5 = S<X..., T>;
+    };
+
+    Inner(trouble_1, V v, Trouble trouble) {}
+    Inner(trouble_2, V v, Trouble2<V> trouble) {}
+    Inner(trouble_3, V v, Trouble3<V> trouble) {}
+    Inner(trouble_4, V v, Unrelated<U, T>::template Trouble2<V> trouble) {}
+    Inner(trouble_5, V v, Unrelated<U, T>::Trouble trouble) {}
+    Inner(trouble_6, V v, Unrelated<V, T>::Trouble trouble) {}
+    Inner(trouble_7, V v, Paranoid<V>::Trouble4 trouble) {}
+    Inner(trouble_8, V v, Paranoid<V>::template Trouble5<V> trouble) {}
+    template <class W>
+    Inner(trouble_9, V v, W w, Paranoid<V>::template Trouble5<W> trouble) {}
+  };
+};
+
+S<int, char> s;
+
+Outer<char, int>::Inner _1(t1, 42, s);
+Outer<char, int>::Inner _2(t2, 42, s);
+Outer<char, int>::Inner _3(t3, 42, s);
+Outer<char, int>::Inner _4(t4, 42, s);
+Outer<char, int>::Inner _5(t5, 42, s);
+Outer<char, int>::Inner _6(t6, 42, s);
+Outer<char, int>::Inner _7(t7, 42, s);
+Outer<char, int>::Inner _8(t8, 42, s);
+Outer<char, int>::Inner _9(t9, 42, 24, s);
+
+} // namespace GH94614

>From 70b754ebebd8210c036e2db0901fcfbf6db422af Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 7 Jun 2024 18:50:16 +0800
Subject: [PATCH 2/5] Add prior 'typename's

---
 clang/test/SemaTemplate/nested-deduction-guides.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/test/SemaTemplate/nested-deduction-guides.cpp b/clang/test/SemaTemplate/nested-deduction-guides.cpp
index ff432173200c7..913042810ae82 100644
--- a/clang/test/SemaTemplate/nested-deduction-guides.cpp
+++ b/clang/test/SemaTemplate/nested-deduction-guides.cpp
@@ -63,13 +63,13 @@ template <class T, class U> struct Outer {
     Inner(trouble_1, V v, Trouble trouble) {}
     Inner(trouble_2, V v, Trouble2<V> trouble) {}
     Inner(trouble_3, V v, Trouble3<V> trouble) {}
-    Inner(trouble_4, V v, Unrelated<U, T>::template Trouble2<V> trouble) {}
-    Inner(trouble_5, V v, Unrelated<U, T>::Trouble trouble) {}
-    Inner(trouble_6, V v, Unrelated<V, T>::Trouble trouble) {}
-    Inner(trouble_7, V v, Paranoid<V>::Trouble4 trouble) {}
-    Inner(trouble_8, V v, Paranoid<V>::template Trouble5<V> trouble) {}
+    Inner(trouble_4, V v, typename Unrelated<U, T>::template Trouble2<V> trouble) {}
+    Inner(trouble_5, V v, typename Unrelated<U, T>::Trouble trouble) {}
+    Inner(trouble_6, V v, typename Unrelated<V, T>::Trouble trouble) {}
+    Inner(trouble_7, V v, typename Paranoid<V>::Trouble4 trouble) {}
+    Inner(trouble_8, V v, typename Paranoid<V>::template Trouble5<V> trouble) {}
     template <class W>
-    Inner(trouble_9, V v, W w, Paranoid<V>::template Trouble5<W> trouble) {}
+    Inner(trouble_9, V v, W w, typename Paranoid<V>::template Trouble5<W> trouble) {}
   };
 };
 

>From f5a196a61b71ce6a002f2a25c26670d7d804cca1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 7 Jun 2024 21:03:52 +0800
Subject: [PATCH 3/5] Give up if we exit a RecordDecl

---
 clang/lib/Sema/SemaTemplate.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 1e921dd26bd7d..baf2f12bcbb07 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2236,14 +2236,16 @@ class ExtractTypeForDeductionGuide
 
   TypeSourceInfo *transform(TypeSourceInfo *TSI) { return TransformType(TSI); }
 
+  /// Returns true if it's safe to substitute \p Typedef with
+  /// \p OuterInstantiationArgs.
   bool mightReferToOuterTemplateParameters(TypedefNameDecl *Typedef) {
     if (!NestedPattern)
       return false;
 
     static auto WalkUp = [](DeclContext *DC, DeclContext *TargetDC) {
-      if (DC == TargetDC)
+      if (DC->Equals(TargetDC))
         return true;
-      while (!DC->isTranslationUnit()) {
+      while (DC->isRecord()) {
         if (DC->Equals(TargetDC))
           return true;
         DC = DC->getParent();

>From fe4a7cc2add1e22bce88782614d49d8ca7be6787 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 15 Jun 2024 20:29:24 +0800
Subject: [PATCH 4/5] [Clang] Instantiate local constexpr functions eagerly

---
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/lib/Sema/SemaExpr.cpp                   |  3 ++-
 .../SemaTemplate/instantiate-local-class.cpp  | 24 +++++++++++++++++++
 3 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cc785d060901a..a5c8a00abd4c8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -848,6 +848,7 @@ Bug Fixes to C++ Support
   (#GH88081), (#GH89496), (#GH90669) and (#GH91633).
 - Fixed handling of brace ellison when building deduction guides. (#GH64625), (#GH83368).
 - Fixed a CTAD substitution bug involving type aliases that reference outer template parameters. (#GH94614).
+- Clang now instantiates local constexpr functions eagerly for constant evaluators. (#GH35052), (#GH94849)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 99a8704298314..88e9b2b00f84d 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18112,7 +18112,8 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
 
         if (FirstInstantiation || TSK != TSK_ImplicitInstantiation ||
             Func->isConstexpr()) {
-          if (isa<CXXRecordDecl>(Func->getDeclContext()) &&
+          if (!Func->isConstexpr() &&
+              isa<CXXRecordDecl>(Func->getDeclContext()) &&
               cast<CXXRecordDecl>(Func->getDeclContext())->isLocalClass() &&
               CodeSynthesisContexts.size())
             PendingLocalImplicitInstantiations.push_back(
diff --git a/clang/test/SemaTemplate/instantiate-local-class.cpp b/clang/test/SemaTemplate/instantiate-local-class.cpp
index 47591045fd26e..7eee131e28d60 100644
--- a/clang/test/SemaTemplate/instantiate-local-class.cpp
+++ b/clang/test/SemaTemplate/instantiate-local-class.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -verify -std=c++11 %s
 // RUN: %clang_cc1 -verify -std=c++11 -fdelayed-template-parsing %s
+// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
 
 template<typename T>
 void f0() {
@@ -509,3 +510,26 @@ namespace LambdaInDefaultMemberInitializer {
   }
   template void f<int>();
 }
+
+#if __cplusplus >= 201703L
+namespace GH35052 {
+
+template <typename F> constexpr int func(F f) {
+  if constexpr (f(1UL)) {
+    return 1;
+  }
+  return 0;
+}
+
+int main() {
+  auto predicate = [](auto v) /*implicit constexpr*/ -> bool {
+    return v == 1;
+  };
+
+  static_assert(predicate(1));
+  return func(predicate);
+}
+
+} // namespace GH35052
+
+#endif

>From c553fd0db2cad16b3b7b68f3459b0c26510ecd21 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 29 Jun 2024 17:47:18 +0800
Subject: [PATCH 5/5] Address review feedback

---
 clang/lib/Sema/SemaTemplate.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7bc46f9efb8ca..15c8385dc1fa0 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2310,7 +2310,8 @@ class ExtractTypeForDeductionGuide
     //       Inner(Alias);
     //     };
     //   };
-    if (OuterInstantiationArgs && InDependentContext) {
+    if (OuterInstantiationArgs && InDependentContext &&
+        TL.getTypePtr()->isInstantiationDependentType()) {
       Decl = cast_if_present<TypedefNameDecl>(SemaRef.SubstDecl(
           OrigDecl, Context.getTranslationUnitDecl(), *OuterInstantiationArgs));
       if (!Decl)
@@ -2667,7 +2668,8 @@ struct ConvertConstructorToDeductionGuideTransform {
       // defined at the class template and the constructor. In this example,
       // they're U and V, respectively.
       NewParam =
-          transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs);
+          transformFunctionTypeParam(NewParam, Args, MaterializedTypedefs,
+                                     /*TransformingOuterPatterns=*/false);
       if (!NewParam)
         return QualType();
       ParamTypes.push_back(NewParam->getType());
@@ -2712,7 +2714,7 @@ struct ConvertConstructorToDeductionGuideTransform {
   ParmVarDecl *transformFunctionTypeParam(
       ParmVarDecl *OldParam, MultiLevelTemplateArgumentList &Args,
       llvm::SmallVectorImpl<TypedefNameDecl *> &MaterializedTypedefs,
-      bool TransformingOuterPatterns = false) {
+      bool TransformingOuterPatterns) {
     TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
     TypeSourceInfo *NewDI;
     if (auto PackTL = OldDI->getTypeLoc().getAs<PackExpansionTypeLoc>()) {



More information about the cfe-commits mailing list