[clang] [clang] CTAD alias: fix transformation for require-clause expr Part2. (PR #93533)

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 4 09:11:04 PDT 2024


https://github.com/hokein updated https://github.com/llvm/llvm-project/pull/93533

>From b0b8d5b064c35e4f0b66e2538c4172afa9665dbe Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Fri, 17 May 2024 15:28:48 +0200
Subject: [PATCH 1/4] [clang] CTAD alias: fix transformation for require-clause
 expr Part2.

In the https://github.com/llvm/llvm-project/pull/90961 fix, we miss a
case where the undeduced template parameters of the underlying deduction
guide is not transformed, which leaves incorrect depth/index
information, and causes crash when evaluating the constraints.

This patch fix this missing case.

Fixes #92596
Fixes #92212
---
 clang/lib/Sema/SemaTemplate.cpp              | 32 ++++++++++++++++----
 clang/test/AST/ast-dump-ctad-alias.cpp       | 25 +++++++++++++++
 clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 25 +++++++++++++++
 3 files changed, 76 insertions(+), 6 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 6879a9a274b5c..08c6858f7e776 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2743,6 +2743,7 @@ Expr *
 buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
                            TypeAliasTemplateDecl *AliasTemplate,
                            ArrayRef<DeducedTemplateArgument> DeduceResults,
+                           unsigned UndeducedTemplateParameterStartIndex,
                            Expr *IsDeducible) {
   Expr *RC = F->getTemplateParameters()->getRequiresClause();
   if (!RC)
@@ -2803,8 +2804,22 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
 
   for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
     const auto &D = DeduceResults[Index];
-    if (D.isNull())
+    if (D.isNull()) { // non-deduced template parameters of f
+      auto TP = F->getTemplateParameters()->getParam(Index);
+      MultiLevelTemplateArgumentList Args;
+      Args.setKind(TemplateSubstitutionKind::Rewrite);
+      Args.addOuterTemplateArguments(TemplateArgsForBuildingRC);
+      // Rebuild the template parameter with updated depth and index.
+      NamedDecl *NewParam = transformTemplateParameter(
+          SemaRef, F->getDeclContext(), TP, Args,
+          /*NewIndex=*/UndeducedTemplateParameterStartIndex++,
+          getTemplateParameterDepth(TP) + AdjustDepth);
+
+      assert(TemplateArgsForBuildingRC[Index].isNull());
+      TemplateArgsForBuildingRC[Index] = Context.getCanonicalTemplateArgument(
+          Context.getInjectedTemplateArg(NewParam));
       continue;
+    }
     TemplateArgumentLoc Input =
         SemaRef.getTrivialTemplateArgumentLoc(D, QualType(), SourceLocation{});
     TemplateArgumentLoc Output;
@@ -2820,9 +2835,11 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
   MultiLevelTemplateArgumentList ArgsForBuildingRC;
   ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite);
   ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC);
-  // For 2), if the underlying F is instantiated from a member template, we need
-  // the entire template argument list, as the constraint AST in the
-  // require-clause of F remains completely uninstantiated.
+  // For 2), if the underlying function template F is nested in a class template
+  // (either instantiated from an explicitly-written deduction guide, or
+  // synthesized from a constructor), we need the entire template argument list,
+  // as the constraint AST in the require-clause of F remains completely
+  // uninstantiated.
   //
   // For example:
   //   template <typename T> // depth 0
@@ -2845,7 +2862,8 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
   // We add the outer template arguments which is [int] to the multi-level arg
   // list to ensure that the occurrence U in `C<U>` will be replaced with int
   // during the substitution.
-  if (F->getInstantiatedFromMemberTemplate()) {
+  if (F->getLexicalDeclContext()->getDeclKind() ==
+      clang::Decl::ClassTemplateSpecialization) {
     auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs(
         F, F->getLexicalDeclContext(),
         /*Final=*/false, /*Innermost=*/std::nullopt,
@@ -3063,6 +3081,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
         Context.getInjectedTemplateArg(NewParam));
     TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument;
   }
+  unsigned UndeducedTemplateParameterStartIndex = FPrimeTemplateParams.size();
   //   ...followed by the template parameters of f that were not deduced
   //   (including their default template arguments)
   for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) {
@@ -3132,7 +3151,8 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
     Expr *IsDeducible = buildIsDeducibleConstraint(
         SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
     Expr *RequiresClause = buildAssociatedConstraints(
-        SemaRef, F, AliasTemplate, DeduceResults, IsDeducible);
+        SemaRef, F, AliasTemplate, DeduceResults,
+        UndeducedTemplateParameterStartIndex, IsDeducible);
 
     auto *FPrimeTemplateParamList = TemplateParameterList::Create(
         Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp
index a4b6f06547443..b9101d88cf8ac 100644
--- a/clang/test/AST/ast-dump-ctad-alias.cpp
+++ b/clang/test/AST/ast-dump-ctad-alias.cpp
@@ -53,6 +53,31 @@ Out2<double>::AInner t(1.0);
 // CHECK-NEXT: |       | `-BuiltinType {{.*}} 'double'
 // CHECK-NEXT: |       `-ParmVarDecl {{.*}} 'double'
 
+// GH92596
+template <typename T0>
+struct Out3 {
+  template<class T1, typename T2>
+  struct Foo {
+    // Deduction guide: Foo(T1, T2, V) -> Foo<T1, T2, V>;
+    template<class V> requires Concept<T0, V> // V in require clause of Foo deduction guide: depth 1, index: 2
+    Foo(V, T1);
+  };
+};
+template<class T3>
+using AFoo3 = Out3<int>::Foo<T3, T3>;
+AFoo3 afoo3{0, 1};
+// Verify occurrence V in the require-clause is transformed (depth: 1 => 0, index: 2 => 1) correctly.
+
+// CHECK:      FunctionTemplateDecl {{.*}} implicit <deduction guide for AFoo3>
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 0 T3
+// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} class depth 0 index 1 V
+// CHECK-NEXT: |-BinaryOperator {{.*}} '<dependent type>' '&&'
+// CHECK-NEXT: | |-UnresolvedLookupExpr {{.*}} '<dependent type>' lvalue (no ADL) = 'Concept'
+// CHECK-NEXT: | | |-TemplateArgument type 'int'
+// CHECK-NEXT: | | | `-BuiltinType {{.*}} 'int'
+// CHECK-NEXT: | | `-TemplateArgument type 'type-parameter-0-1'
+// CHECK-NEXT: | |   `-TemplateTypeParmType {{.*}} 'type-parameter-0-1' dependent depth 0 index 1
+
 template <typename... T1>
 struct Foo {
   Foo(T1...);
diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index 3dafe823fbbaa..aa68a088d4f7b 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -414,4 +414,29 @@ struct A1 {
 template <typename U>
 using AFoo = A1<int>::A2<int>::Foo<U>;
 AFoo case3(1);
+
+// Case4: crashes on the constexpr evaluator due to the mixed-up index for the
+// template parameters `V`.
+template<class T, typename T2>
+struct Case4 {
+  template<class V> requires C<V>
+  Case4(V, T);
+};
+
+template<class T2>
+using ACase4 = Case4<T2, T2>;
+ACase4 case4{0, 1};
+
 } // namespace test24
+
+namespace GH92212 {
+template<typename T, typename...Us>
+struct A{
+  template<typename V> requires __is_same(V, int)
+  A(V);
+};
+
+template<typename...TS>
+using AA = A<int, TS...>;
+AA a{0};
+}

>From 2447bf860b74119f90639838c3e95eefbd12972b Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Thu, 27 Jun 2024 10:20:57 +0200
Subject: [PATCH 2/4] Address review comments.

---
 clang/lib/Sema/SemaTemplate.cpp        | 25 ++++++++++++++-----------
 clang/test/AST/ast-dump-ctad-alias.cpp |  4 +++-
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 08c6858f7e776..62a41b29364cf 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2743,7 +2743,7 @@ Expr *
 buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
                            TypeAliasTemplateDecl *AliasTemplate,
                            ArrayRef<DeducedTemplateArgument> DeduceResults,
-                           unsigned UndeducedTemplateParameterStartIndex,
+                           unsigned FirstUndeducedParamIdx,
                            Expr *IsDeducible) {
   Expr *RC = F->getTemplateParameters()->getRequiresClause();
   if (!RC)
@@ -2805,16 +2805,16 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
   for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
     const auto &D = DeduceResults[Index];
     if (D.isNull()) { // non-deduced template parameters of f
-      auto TP = F->getTemplateParameters()->getParam(Index);
+      NamedDecl* TP = F->getTemplateParameters()->getParam(Index);
       MultiLevelTemplateArgumentList Args;
       Args.setKind(TemplateSubstitutionKind::Rewrite);
       Args.addOuterTemplateArguments(TemplateArgsForBuildingRC);
       // Rebuild the template parameter with updated depth and index.
       NamedDecl *NewParam = transformTemplateParameter(
           SemaRef, F->getDeclContext(), TP, Args,
-          /*NewIndex=*/UndeducedTemplateParameterStartIndex++,
+          /*NewIndex=*/FirstUndeducedParamIdx,
           getTemplateParameterDepth(TP) + AdjustDepth);
-
+      FirstUndeducedParamIdx += 1;
       assert(TemplateArgsForBuildingRC[Index].isNull());
       TemplateArgsForBuildingRC[Index] = Context.getCanonicalTemplateArgument(
           Context.getInjectedTemplateArg(NewParam));
@@ -2835,11 +2835,9 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
   MultiLevelTemplateArgumentList ArgsForBuildingRC;
   ArgsForBuildingRC.setKind(clang::TemplateSubstitutionKind::Rewrite);
   ArgsForBuildingRC.addOuterTemplateArguments(TemplateArgsForBuildingRC);
-  // For 2), if the underlying function template F is nested in a class template
-  // (either instantiated from an explicitly-written deduction guide, or
-  // synthesized from a constructor), we need the entire template argument list,
-  // as the constraint AST in the require-clause of F remains completely
-  // uninstantiated.
+  // For 2), if the underlying deduction guide F is nested in a class template,
+  // we need the entire template argument list, as the constraint AST in the
+  // require-clause of F remains completely uninstantiated.
   //
   // For example:
   //   template <typename T> // depth 0
@@ -2862,6 +2860,11 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
   // We add the outer template arguments which is [int] to the multi-level arg
   // list to ensure that the occurrence U in `C<U>` will be replaced with int
   // during the substitution.
+  //
+  // NOTE: The underlying deduction guide F is instantiated -- either from an
+  // explicitly-written deduction guide member, or from a constructor.
+  // getInstantiatedFromMemberTemplate() can only handle the former case, so we
+  // check the DeclContext kind.
   if (F->getLexicalDeclContext()->getDeclKind() ==
       clang::Decl::ClassTemplateSpecialization) {
     auto OuterLevelArgs = SemaRef.getTemplateInstantiationArgs(
@@ -3081,7 +3084,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
         Context.getInjectedTemplateArg(NewParam));
     TransformedDeducedAliasArgs[AliasTemplateParamIdx] = NewTemplateArgument;
   }
-  unsigned UndeducedTemplateParameterStartIndex = FPrimeTemplateParams.size();
+  unsigned FirstUndeducedParamIdx = FPrimeTemplateParams.size();
   //   ...followed by the template parameters of f that were not deduced
   //   (including their default template arguments)
   for (unsigned FTemplateParamIdx : NonDeducedTemplateParamsInFIndex) {
@@ -3152,7 +3155,7 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
         SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
     Expr *RequiresClause = buildAssociatedConstraints(
         SemaRef, F, AliasTemplate, DeduceResults,
-        UndeducedTemplateParameterStartIndex, IsDeducible);
+        FirstUndeducedParamIdx, IsDeducible);
 
     auto *FPrimeTemplateParamList = TemplateParameterList::Create(
         Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),
diff --git a/clang/test/AST/ast-dump-ctad-alias.cpp b/clang/test/AST/ast-dump-ctad-alias.cpp
index b9101d88cf8ac..6f07a62e9a069 100644
--- a/clang/test/AST/ast-dump-ctad-alias.cpp
+++ b/clang/test/AST/ast-dump-ctad-alias.cpp
@@ -58,7 +58,9 @@ template <typename T0>
 struct Out3 {
   template<class T1, typename T2>
   struct Foo {
-    // Deduction guide: Foo(T1, T2, V) -> Foo<T1, T2, V>;
+    // Deduction guide:
+    //   template <typename T1, typename T2, typename V>
+    //   Foo(V, T1) -> Foo<T1, T2>;
     template<class V> requires Concept<T0, V> // V in require clause of Foo deduction guide: depth 1, index: 2
     Foo(V, T1);
   };

>From 251f179e1fc654381b90a2746799c044344499f3 Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Thu, 4 Jul 2024 17:58:47 +0200
Subject: [PATCH 3/4] Rebase and update the test

---
 clang/test/SemaCXX/cxx20-ctad-type-alias.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
index aa68a088d4f7b..a369ce687a728 100644
--- a/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
+++ b/clang/test/SemaCXX/cxx20-ctad-type-alias.cpp
@@ -266,7 +266,7 @@ template <typename U>
 using Bar = Foo<U>; // expected-note {{could not match 'Foo<type-parameter-0-0>' against 'int'}} \
                     // expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar(Foo<type-parameter-0-0>) -> Foo<type-parameter-0-0>'}} \
                     // expected-note {{candidate template ignored: constraints not satisfied}} \
-                    // expected-note {{implicit deduction guide declared as 'template <typename T> requires False<T> && __is_deducible(test18::Bar, Foo<int>) Bar(type-parameter-0-0) -> Foo<int>'}} \
+                    // expected-note {{implicit deduction guide declared as 'template <typename T> requires False<type-parameter-0-0> && __is_deducible(test18::Bar, Foo<int>) Bar(type-parameter-0-0) -> Foo<int>'}} \
                     // expected-note {{candidate function template not viable}} \
                     // expected-note {{implicit deduction guide declared as 'template <typename U> requires __is_deducible(test18::Bar, Foo<type-parameter-0-0>) Bar() -> Foo<type-parameter-0-0>'}}
 

>From 284d1171ad24154ecd847d32b63ec59d7aabbdde Mon Sep 17 00:00:00 2001
From: Haojian Wu <hokein.wu at gmail.com>
Date: Thu, 4 Jul 2024 18:10:41 +0200
Subject: [PATCH 4/4] clang-format

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

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 62a41b29364cf..07b3f793b3a29 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2743,8 +2743,7 @@ Expr *
 buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
                            TypeAliasTemplateDecl *AliasTemplate,
                            ArrayRef<DeducedTemplateArgument> DeduceResults,
-                           unsigned FirstUndeducedParamIdx,
-                           Expr *IsDeducible) {
+                           unsigned FirstUndeducedParamIdx, Expr *IsDeducible) {
   Expr *RC = F->getTemplateParameters()->getRequiresClause();
   if (!RC)
     return IsDeducible;
@@ -2805,7 +2804,7 @@ buildAssociatedConstraints(Sema &SemaRef, FunctionTemplateDecl *F,
   for (unsigned Index = 0; Index < DeduceResults.size(); ++Index) {
     const auto &D = DeduceResults[Index];
     if (D.isNull()) { // non-deduced template parameters of f
-      NamedDecl* TP = F->getTemplateParameters()->getParam(Index);
+      NamedDecl *TP = F->getTemplateParameters()->getParam(Index);
       MultiLevelTemplateArgumentList Args;
       Args.setKind(TemplateSubstitutionKind::Rewrite);
       Args.addOuterTemplateArguments(TemplateArgsForBuildingRC);
@@ -3153,9 +3152,9 @@ BuildDeductionGuideForTypeAlias(Sema &SemaRef,
 
     Expr *IsDeducible = buildIsDeducibleConstraint(
         SemaRef, AliasTemplate, FPrime->getReturnType(), FPrimeTemplateParams);
-    Expr *RequiresClause = buildAssociatedConstraints(
-        SemaRef, F, AliasTemplate, DeduceResults,
-        FirstUndeducedParamIdx, IsDeducible);
+    Expr *RequiresClause =
+        buildAssociatedConstraints(SemaRef, F, AliasTemplate, DeduceResults,
+                                   FirstUndeducedParamIdx, IsDeducible);
 
     auto *FPrimeTemplateParamList = TemplateParameterList::Create(
         Context, AliasTemplate->getTemplateParameters()->getTemplateLoc(),



More information about the cfe-commits mailing list