[clang] [Clang] Correctly construct template arguments for template template parameters (PR #76811)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 6 05:03:52 PST 2024


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

>From e017bed2ee443d72f445f584dd6356bf151c5d79 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Wed, 3 Jan 2024 19:33:01 +0800
Subject: [PATCH 1/3] [Clang] Correctly construct template arguments for
 file-scope template template parameters

This fixes the bug introduced by
https://github.com/llvm/llvm-project/commit/6db007a0654ed7a6ed5c3aa3b61a937c19a6bc6b.

We construct placeholder template arguments for template-template parameters to
avoid mismatching argument substitution since they have different depths
with their corresponding template arguments. In this case,

```cpp
template <template <Concept C> class T> void foo(T<int>);
```

T lies at the depth 0, and C lies at 1. The corresponding argument, of which
there is exactly one, int, is at depth 0. If we consider the
argument as the outermost one, then we would end up substituting 'int'
into the wrong parameter T.

We used to perform such placeholder construction during the context walk-up.
In the previous patch, we slipped through that inadvertently because we would
walk up to the parent, which is precisely a FileContext for template-template
parameters, after adding innermost arguments.

Besides, this patch moves the sanity check up to the context switch.
That way, we avoid dereferencing null pointers if ND is unspecified.

Closes https://github.com/llvm/llvm-project/issues/57410.
---
 clang/docs/ReleaseNotes.rst                   |  5 ++++
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 12 ++++++---
 .../temp/temp.arg/temp.arg.template/p3-2a.cpp | 25 +++++++++++++++++++
 3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9b6e00b231216b..154412144ef97a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -857,6 +857,11 @@ Bug Fixes to C++ Support
   (`#64607 <https://github.com/llvm/llvm-project/issues/64607>`_)
   (`#64086 <https://github.com/llvm/llvm-project/issues/64086>`_)
 
+- Fixed a regression where clang forgets how to substitute into constraints on template-template
+  parameters. Fixes: 
+  (`#57410 <https://github.com/llvm/llvm-project/issues/57410>`_) and
+  (`#76604 <https://github.com/llvm/llvm-project/issues/57410>`_)
+
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 - Fixed an import failure of recursive friend class template.
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 09dd119ed1b948..c4aae8298d6122 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -344,15 +344,19 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
 
   using namespace TemplateInstArgsHelpers;
   const Decl *CurDecl = ND;
+
+  if (!ND)
+    CurDecl = Decl::castFromDeclContext(DC);
+
   if (Innermost) {
     Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND),
                                      Innermost->asArray(), Final);
-    CurDecl = Response::UseNextDecl(ND).NextDecl;
+    if (CurDecl->getDeclContext()->isFileContext())
+      if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
+        HandleDefaultTempArgIntoTempTempParam(TTP, Result);
+    CurDecl = Response::UseNextDecl(CurDecl).NextDecl;
   }
 
-  if (!ND)
-    CurDecl = Decl::castFromDeclContext(DC);
-
   while (!CurDecl->isFileContextDecl()) {
     Response R;
     if (const auto *VarTemplSpec =
diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
index 449b6232542e24..277935f6b3b2f0 100644
--- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
+++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
@@ -59,3 +59,28 @@ struct Nothing {};
 
 // FIXME: Wait the standard to clarify the intent.
 template<> template<> Z<Nothing> S5<Z>::V<Nothing>;
+
+namespace GH57410 {
+
+template<typename T>
+concept True = true;
+
+template<typename T>
+concept False = false; // #False
+
+template<template<True T> typename Wrapper>
+using Test = Wrapper<int>;
+
+template<template<False T> typename Wrapper> // #TTP-Wrapper
+using Test = Wrapper<int>; // expected-error {{constraints not satisfied for template template parameter 'Wrapper' [with T = int]}}
+
+// expected-note@#TTP-Wrapper {{'int' does not satisfy 'False'}}
+// expected-note@#False {{evaluated to false}}
+
+template <template<False> typename T> // #TTP-foo
+void foo(T<int>); // expected-error {{constraints not satisfied for template template parameter 'T' [with $0 = int]}}
+
+// expected-note@#TTP-foo {{'int' does not satisfy 'False'}}
+// expected-note@#False {{evaluated to false}}
+
+}

>From 5aa6a924f33776521cb30ac081170a156e124f96 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 5 Jan 2024 16:37:05 +0800
Subject: [PATCH 2/3] Address comments and poke the CI

---
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 22 ++++++++++++-------
 .../temp/temp.arg/temp.arg.template/p3-2a.cpp | 12 ++++++----
 2 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index c4aae8298d6122..c3f893f51204be 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -345,15 +345,23 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
   using namespace TemplateInstArgsHelpers;
   const Decl *CurDecl = ND;
 
-  if (!ND)
+  if (!CurDecl)
     CurDecl = Decl::castFromDeclContext(DC);
 
   if (Innermost) {
     Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND),
                                      Innermost->asArray(), Final);
-    if (CurDecl->getDeclContext()->isFileContext())
-      if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
-        HandleDefaultTempArgIntoTempTempParam(TTP, Result);
+    // Populate placeholder template arguments for TemplateTemplateParmDecls
+    // that live in a file-scope DeclContext. This is essential for the case
+    // e.g.
+    //
+    // template <class> concept Concept = false;
+    // template <template <Concept C> class T> void foo(T<int>)
+    //
+    // where parameter C has a depth of 1 but the substituting argument `int`
+    // has a depth of 0.
+    if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl))
+      HandleDefaultTempArgIntoTempTempParam(TTP, Result);
     CurDecl = Response::UseNextDecl(CurDecl).NextDecl;
   }
 
@@ -384,10 +392,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
       R = Response::ChangeDecl(CTD->getLexicalDeclContext());
     } else if (!isa<DeclContext>(CurDecl)) {
       R = Response::DontClearRelativeToPrimaryNextDecl(CurDecl);
-      if (CurDecl->getDeclContext()->isTranslationUnit()) {
-        if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) {
-          R = HandleDefaultTempArgIntoTempTempParam(TTP, Result);
-        }
+      if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(CurDecl)) {
+        R = HandleDefaultTempArgIntoTempTempParam(TTP, Result);
       }
     } else {
       R = HandleGenericDeclContext(CurDecl);
diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
index 277935f6b3b2f0..f586069638614b 100644
--- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
+++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
@@ -68,6 +68,8 @@ concept True = true;
 template<typename T>
 concept False = false; // #False
 
+template <class> struct S {};
+
 template<template<True T> typename Wrapper>
 using Test = Wrapper<int>;
 
@@ -77,10 +79,12 @@ using Test = Wrapper<int>; // expected-error {{constraints not satisfied for tem
 // expected-note@#TTP-Wrapper {{'int' does not satisfy 'False'}}
 // expected-note@#False {{evaluated to false}}
 
-template <template<False> typename T> // #TTP-foo
-void foo(T<int>); // expected-error {{constraints not satisfied for template template parameter 'T' [with $0 = int]}}
+template <typename U, template<False> typename T>
+void foo(T<U>); // #foo
 
-// expected-note@#TTP-foo {{'int' does not satisfy 'False'}}
-// expected-note@#False {{evaluated to false}}
+void bar() {
+  foo<int>(S<int>{}); // expected-error {{no matching function for call to 'foo'}}
+  // expected-note@#foo {{substitution failure [with U = int]: constraints not satisfied for template template parameter 'T' [with $0 = int]}}
+}
 
 }

>From 323a3ec564da5018e6f7f2be4072b37c0ca49e5a Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 6 Jan 2024 21:03:17 +0800
Subject: [PATCH 3/3] fixup

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

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index c3f893f51204be..57e005a0119f57 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -351,9 +351,8 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
   if (Innermost) {
     Result.addOuterTemplateArguments(const_cast<NamedDecl *>(ND),
                                      Innermost->asArray(), Final);
-    // Populate placeholder template arguments for TemplateTemplateParmDecls
-    // that live in a file-scope DeclContext. This is essential for the case
-    // e.g.
+    // Populate placeholder template arguments for TemplateTemplateParmDecls. 
+    // This is essential for the case e.g.
     //
     // template <class> concept Concept = false;
     // template <template <Concept C> class T> void foo(T<int>)



More information about the cfe-commits mailing list