[clang] [Clang] Transform SubstNonTypeTemplateParmExpr replacements in a constant-evaluated context (PR #196791)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 4 20:00:17 PDT 2026


https://github.com/wx257osn2 updated https://github.com/llvm/llvm-project/pull/196791

>From 437a92eb22f392fa6b724dcdfcb2e4e8cf5922b3 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Sun, 10 May 2026 18:12:15 +0900
Subject: [PATCH 01/15] add test

https://github.com/llvm/llvm-project/issues/175831#issuecomment-3753235470
---
 clang/test/SemaTemplate/concepts.cpp | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index b427e6a4d2fa5..4275f63989607 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1851,7 +1851,6 @@ namespace GH191016 {
   void test(){ S<int> s; }
 }
 
-
 namespace GH188640 {
 
 namespace Ex1 {
@@ -2002,3 +2001,21 @@ namespace GH196375 {
   static_assert(f<4>());
   // expected-error at -1 {{no matching function for call to 'f'}}
 } // namespace GHGH196375
+
+namespace GH175831 {
+
+template<class>
+struct reference {};
+template<class Q>
+consteval Q get_spec(reference<Q>) { return {}; }
+
+template<class T>
+concept repr_impl = sizeof(T) > 0;
+template<class, auto V>
+concept representation_of = repr_impl<decltype(V)>;
+template<auto V, representation_of<get_spec(V)>>
+struct quantity {};
+
+auto x = quantity<reference<int>{}, int>{};
+
+} // namespace GH175831

>From 744696100cb96cc5b7c4cddde7cb99511778a447 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Sun, 10 May 2026 18:21:56 +0900
Subject: [PATCH 02/15] stop replacement on unevaluated context

---
 clang/lib/Sema/TreeTransform.h | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 70cdc47e42d5d..34b0ac9e86be5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16727,13 +16727,23 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     // specific annotations, such as implicit casts, are discarded. Calling the
     // corresponding sema action is necessary to recover those. Otherwise,
     // equivalency of the result would be lost.
+    //
+    // In unevaluated contexts (e.g. inside decltype), CheckTemplateArgument
+    // forces constant evaluation that is inappropriate and may fail for
+    // valid expressions (e.g. function calls with by-value class parameters).
+    // Since we only need the type in such contexts, we can tolerate the
+    // failure and proceed with the transformed replacement as-is.
     TemplateArgument SugaredConverted, CanonicalConverted;
-    Replacement = SemaRef.CheckTemplateArgument(
+    ExprResult Checked = SemaRef.CheckTemplateArgument(
         Param, ParamType, Replacement.get(), SugaredConverted,
         CanonicalConverted,
         /*StrictCheck=*/false, Sema::CTAK_Specified);
-    if (Replacement.isInvalid())
-      return true;
+    if (Checked.isInvalid()) {
+      if (!SemaRef.isUnevaluatedContext())
+        return true;
+    } else {
+      Replacement = Checked;
+    }
   } else {
     // Otherwise, the same expression would have been produced.
     Replacement = E->getReplacement();

>From 773d1db889a91bf9c76ba4077e636be3a8bd7c95 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Sun, 10 May 2026 18:47:44 +0900
Subject: [PATCH 03/15] add release note

---
 clang/docs/ReleaseNotes.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9e4a47a5b18fc..a654a94adceb4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -655,6 +655,7 @@ Bug Fixes in This Version
 - Fixed a crash when ``#embed`` is used with C++ modules (#GH195350)
 - Fixed an issue where ``__typeof_unqual`` and ``__typeof_unqual__`` were rejected as a declaration specifier in block scope in C++.
 - Fixed crash when checking for overflow for unary operator that can't overflow (#GH170072)
+- Fixed a regression where calling a function that takes a class-type parameter by value inside ``decltype`` of a concept could be incorrectly rejected when used as a non-type template argument. (#GH175831)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

>From 1ffdb2bab511789c86fb1250a8afed0bfc402276 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Mon, 11 May 2026 20:49:25 +0900
Subject: [PATCH 04/15] add test

---
 clang/test/SemaTemplate/concepts.cpp | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 4275f63989607..794229cf0b1db 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -2004,6 +2004,8 @@ namespace GH196375 {
 
 namespace GH175831 {
 
+namespace ShuoldResolve {
+
 template<class>
 struct reference {};
 template<class Q>
@@ -2018,4 +2020,28 @@ struct quantity {};
 
 auto x = quantity<reference<int>{}, int>{};
 
+} // namespace ShouldResolve
+
+namespace CannotResolve {
+
+template<class>
+struct reference {};
+template<class Q>
+consteval auto get_spec(reference<Q>) { return Q{}; }
+
+template<class T>
+concept repr_impl = sizeof(T) > sizeof(char);
+template<class, auto V>
+concept representation_of = repr_impl<decltype(V)>;
+template<auto V, representation_of<get_spec(V)>>
+struct quantity {};
+
+auto x = quantity<reference<char>{}, char>{};
+// expected-error at -1 {{constraints not satisfied for class template 'quantity' [with V = reference<char>{}, $1 = char]}}
+// expected-note at -5  {{because 'representation_of<char, get_spec(reference<char>{})>' evaluated to false}}
+// expected-note-re at -7  {{because 'decltype({{.*}})' (aka 'char') does not satisfy 'repr_impl'}}
+// expected-note at -10  {{because 'sizeof(char) > sizeof(char)' (1 > 1) evaluated to false}}
+
+} // namespace CannotResolve
+
 } // namespace GH175831

>From 27a39f00495b8ffa07bc1797df7ac4aa8a760de2 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Mon, 11 May 2026 20:57:21 +0900
Subject: [PATCH 05/15] check evaluated context or not before calling
 CheckTemplateArgument

---
 clang/lib/Sema/TreeTransform.h | 22 +++++++---------------
 1 file changed, 7 insertions(+), 15 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 34b0ac9e86be5..8c661ce27fc4c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16727,22 +16727,14 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     // specific annotations, such as implicit casts, are discarded. Calling the
     // corresponding sema action is necessary to recover those. Otherwise,
     // equivalency of the result would be lost.
-    //
-    // In unevaluated contexts (e.g. inside decltype), CheckTemplateArgument
-    // forces constant evaluation that is inappropriate and may fail for
-    // valid expressions (e.g. function calls with by-value class parameters).
-    // Since we only need the type in such contexts, we can tolerate the
-    // failure and proceed with the transformed replacement as-is.
-    TemplateArgument SugaredConverted, CanonicalConverted;
-    ExprResult Checked = SemaRef.CheckTemplateArgument(
-        Param, ParamType, Replacement.get(), SugaredConverted,
-        CanonicalConverted,
-        /*StrictCheck=*/false, Sema::CTAK_Specified);
-    if (Checked.isInvalid()) {
-      if (!SemaRef.isUnevaluatedContext())
+    if (!SemaRef.isUnevaluatedContext()) {
+      TemplateArgument SugaredConverted, CanonicalConverted;
+      Replacement = SemaRef.CheckTemplateArgument(
+          Param, ParamType, Replacement.get(), SugaredConverted,
+          CanonicalConverted,
+          /*StrictCheck=*/false, Sema::CTAK_Specified);
+      if (Replacement.isInvalid())
         return true;
-    } else {
-      Replacement = Checked;
     }
   } else {
     // Otherwise, the same expression would have been produced.

>From e216ae235080d9d0ae1e2374774d913faf8ab8ff Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Wed, 3 Jun 2026 19:39:36 +0900
Subject: [PATCH 06/15] Revert "check evaluated context or not before calling
 CheckTemplateArgument"

This reverts commit 27a39f00495b8ffa07bc1797df7ac4aa8a760de2.
---
 clang/lib/Sema/TreeTransform.h | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8c661ce27fc4c..34b0ac9e86be5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16727,14 +16727,22 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     // specific annotations, such as implicit casts, are discarded. Calling the
     // corresponding sema action is necessary to recover those. Otherwise,
     // equivalency of the result would be lost.
-    if (!SemaRef.isUnevaluatedContext()) {
-      TemplateArgument SugaredConverted, CanonicalConverted;
-      Replacement = SemaRef.CheckTemplateArgument(
-          Param, ParamType, Replacement.get(), SugaredConverted,
-          CanonicalConverted,
-          /*StrictCheck=*/false, Sema::CTAK_Specified);
-      if (Replacement.isInvalid())
+    //
+    // In unevaluated contexts (e.g. inside decltype), CheckTemplateArgument
+    // forces constant evaluation that is inappropriate and may fail for
+    // valid expressions (e.g. function calls with by-value class parameters).
+    // Since we only need the type in such contexts, we can tolerate the
+    // failure and proceed with the transformed replacement as-is.
+    TemplateArgument SugaredConverted, CanonicalConverted;
+    ExprResult Checked = SemaRef.CheckTemplateArgument(
+        Param, ParamType, Replacement.get(), SugaredConverted,
+        CanonicalConverted,
+        /*StrictCheck=*/false, Sema::CTAK_Specified);
+    if (Checked.isInvalid()) {
+      if (!SemaRef.isUnevaluatedContext())
         return true;
+    } else {
+      Replacement = Checked;
     }
   } else {
     // Otherwise, the same expression would have been produced.

>From 4fe38a26d7247761331aefdb29f46525fa22d239 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Wed, 3 Jun 2026 19:39:42 +0900
Subject: [PATCH 07/15] Revert "stop replacement on unevaluated context"

This reverts commit 744696100cb96cc5b7c4cddde7cb99511778a447.
---
 clang/lib/Sema/TreeTransform.h | 16 +++-------------
 1 file changed, 3 insertions(+), 13 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 34b0ac9e86be5..70cdc47e42d5d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16727,23 +16727,13 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     // specific annotations, such as implicit casts, are discarded. Calling the
     // corresponding sema action is necessary to recover those. Otherwise,
     // equivalency of the result would be lost.
-    //
-    // In unevaluated contexts (e.g. inside decltype), CheckTemplateArgument
-    // forces constant evaluation that is inappropriate and may fail for
-    // valid expressions (e.g. function calls with by-value class parameters).
-    // Since we only need the type in such contexts, we can tolerate the
-    // failure and proceed with the transformed replacement as-is.
     TemplateArgument SugaredConverted, CanonicalConverted;
-    ExprResult Checked = SemaRef.CheckTemplateArgument(
+    Replacement = SemaRef.CheckTemplateArgument(
         Param, ParamType, Replacement.get(), SugaredConverted,
         CanonicalConverted,
         /*StrictCheck=*/false, Sema::CTAK_Specified);
-    if (Checked.isInvalid()) {
-      if (!SemaRef.isUnevaluatedContext())
-        return true;
-    } else {
-      Replacement = Checked;
-    }
+    if (Replacement.isInvalid())
+      return true;
   } else {
     // Otherwise, the same expression would have been produced.
     Replacement = E->getReplacement();

>From 76664d4ecc4c13050b4ee6c53820e814a208767f Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Wed, 3 Jun 2026 19:45:54 +0900
Subject: [PATCH 08/15] fix typo

---
 clang/test/SemaTemplate/concepts.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index 794229cf0b1db..f735bfc1251c2 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -2004,7 +2004,7 @@ namespace GH196375 {
 
 namespace GH175831 {
 
-namespace ShuoldResolve {
+namespace ShouldResolve {
 
 template<class>
 struct reference {};

>From 512b3cc3d63f7e75d51a459d247ae458c9b42d9c Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Wed, 3 Jun 2026 19:42:55 +0900
Subject: [PATCH 09/15] add SkipConstantEvaluation option for
 CheckTemplateArgument

---
 clang/include/clang/Sema/Sema.h | 11 ++++++++++-
 clang/lib/Sema/SemaTemplate.cpp |  8 +++++---
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index b8d760e7e0975..d3eef97811cbd 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12198,12 +12198,21 @@ class Sema final : public SemaBase {
   /// If an error occurred, it returns ExprError(); otherwise, it
   /// returns the converted template argument. \p ParamType is the
   /// type of the non-type template parameter after it has been instantiated.
+  ///
+  /// If \p SkipConstantEvaluation is true, the argument is converted to the
+  /// parameter type, but is not required to be constant-evaluable, similarly
+  /// to a value-dependent argument. This is used when only the converted form
+  /// of the argument is needed, e.g. when re-checking the replacement of a
+  /// SubstNonTypeTemplateParmExpr in an unevaluated context, where constant
+  /// evaluation may fail for otherwise valid arguments because the entities
+  /// they refer to are not odr-used.
   ExprResult CheckTemplateArgument(NamedDecl *Param,
                                    QualType InstantiatedParamType, Expr *Arg,
                                    TemplateArgument &SugaredConverted,
                                    TemplateArgument &CanonicalConverted,
                                    bool StrictCheck,
-                                   CheckTemplateArgumentKind CTAK);
+                                   CheckTemplateArgumentKind CTAK,
+                                   bool SkipConstantEvaluation = false);
 
   /// Check a template argument against its corresponding
   /// template template parameter.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 8c94a1ad39208..9c53d1501fea1 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7140,7 +7140,8 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
                                        TemplateArgument &SugaredConverted,
                                        TemplateArgument &CanonicalConverted,
                                        bool StrictCheck,
-                                       CheckTemplateArgumentKind CTAK) {
+                                       CheckTemplateArgumentKind CTAK,
+                                       bool SkipConstantEvaluation) {
   SourceLocation StartLoc = Arg->getBeginLoc();
   auto *ArgPE = dyn_cast<PackExpansionExpr>(Arg);
   Expr *DeductionArg = ArgPE ? ArgPE->getPattern() : Arg;
@@ -7344,8 +7345,9 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
     }
 
     // For a value-dependent argument, CheckConvertedConstantExpression is
-    // permitted (and expected) to be unable to determine a value.
-    if (ArgResult.get()->isValueDependent()) {
+    // permitted (and expected) to be unable to determine a value. Treat the
+    // argument the same way when the caller does not need the value.
+    if (ArgResult.get()->isValueDependent() || SkipConstantEvaluation) {
       setDeductionArg(ArgResult.get());
       SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
       CanonicalConverted =

>From bf8a90e40c66b1100f6f19a846ef82eb6ffbc390 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Wed, 3 Jun 2026 19:43:20 +0900
Subject: [PATCH 10/15] skip constant evaluation if unevaluated context

---
 clang/lib/Sema/TreeTransform.h | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 70cdc47e42d5d..c3e3bdcad14fe 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16727,11 +16727,19 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     // specific annotations, such as implicit casts, are discarded. Calling the
     // corresponding sema action is necessary to recover those. Otherwise,
     // equivalency of the result would be lost.
+    //
+    // In an unevaluated context, skip the constant evaluation of the
+    // replacement: only its converted form matters there, and the evaluation
+    // can spuriously fail for otherwise valid replacements, because the
+    // entities they refer to are not odr-used in such a context (e.g. the
+    // implicit copy of a function argument of class type cannot be constant
+    // folded when the special members were never instantiated).
     TemplateArgument SugaredConverted, CanonicalConverted;
     Replacement = SemaRef.CheckTemplateArgument(
         Param, ParamType, Replacement.get(), SugaredConverted,
         CanonicalConverted,
-        /*StrictCheck=*/false, Sema::CTAK_Specified);
+        /*StrictCheck=*/false, Sema::CTAK_Specified,
+        /*SkipConstantEvaluation=*/SemaRef.isUnevaluatedContext());
     if (Replacement.isInvalid())
       return true;
   } else {

>From e10169fae91325c593ce09e82568d3f3a80114f9 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Thu, 4 Jun 2026 10:18:09 +0900
Subject: [PATCH 11/15] add test

---
 clang/test/SemaTemplate/concepts.cpp | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index f735bfc1251c2..6f7f00bf12e61 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++20 -ferror-limit 0 -verify=expected,cxx20 %s
-// RUN: %clang_cc1 -std=c++2c -ferror-limit 0 -verify=expected %s
+// RUN: %clang_cc1 -std=c++20 -ferror-limit 0 -fexceptions -fcxx-exceptions -verify=expected,cxx20 %s
+// RUN: %clang_cc1 -std=c++2c -ferror-limit 0 -fexceptions -fcxx-exceptions -verify=expected %s
 
 namespace PR47043 {
   template<typename T> concept True = true;
@@ -2022,7 +2022,7 @@ auto x = quantity<reference<int>{}, int>{};
 
 } // namespace ShouldResolve
 
-namespace CannotResolve {
+namespace CannotResolve0 {
 
 template<class>
 struct reference {};
@@ -2042,6 +2042,25 @@ auto x = quantity<reference<char>{}, char>{};
 // expected-note-re at -7  {{because 'decltype({{.*}})' (aka 'char') does not satisfy 'repr_impl'}}
 // expected-note at -10  {{because 'sizeof(char) > sizeof(char)' (1 > 1) evaluated to false}}
 
-} // namespace CannotResolve
+} // namespace CannotResolve0
+
+namespace CannotResolve1 {
+
+template<class>
+struct reference {};
+template<class Q>
+consteval Q get_spec(reference<Q>) { throw; }
+
+template<class T>
+concept repr_impl = sizeof(T) > 0;
+template<class, auto V>
+concept representation_of = repr_impl<decltype(V)>;
+template<auto V, representation_of<get_spec(V)>>
+struct quantity {};
+
+auto x = quantity<reference<int>{}, int>{};
+// expected-error at -1 {{constraints not satisfied for class template 'quantity' [with V = reference<int>{}, $1 = int]}}
+
+} // namespace CannotResolve1
 
 } // namespace GH175831

>From b8a548f6c62b1d569ec8f7b7ec7bf073371eefbc Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 4 Jun 2026 10:21:40 +0900
Subject: [PATCH 12/15] Revert "skip constant evaluation if unevaluated
 context"

This reverts commit bf8a90e40c66b1100f6f19a846ef82eb6ffbc390.
---
 clang/lib/Sema/TreeTransform.h | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c3e3bdcad14fe..70cdc47e42d5d 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16727,19 +16727,11 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     // specific annotations, such as implicit casts, are discarded. Calling the
     // corresponding sema action is necessary to recover those. Otherwise,
     // equivalency of the result would be lost.
-    //
-    // In an unevaluated context, skip the constant evaluation of the
-    // replacement: only its converted form matters there, and the evaluation
-    // can spuriously fail for otherwise valid replacements, because the
-    // entities they refer to are not odr-used in such a context (e.g. the
-    // implicit copy of a function argument of class type cannot be constant
-    // folded when the special members were never instantiated).
     TemplateArgument SugaredConverted, CanonicalConverted;
     Replacement = SemaRef.CheckTemplateArgument(
         Param, ParamType, Replacement.get(), SugaredConverted,
         CanonicalConverted,
-        /*StrictCheck=*/false, Sema::CTAK_Specified,
-        /*SkipConstantEvaluation=*/SemaRef.isUnevaluatedContext());
+        /*StrictCheck=*/false, Sema::CTAK_Specified);
     if (Replacement.isInvalid())
       return true;
   } else {

>From 0966706862132a8d0851b61d7b9cb730813ceef8 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Thu, 4 Jun 2026 10:21:42 +0900
Subject: [PATCH 13/15] Revert "add SkipConstantEvaluation option for
 CheckTemplateArgument"

This reverts commit 512b3cc3d63f7e75d51a459d247ae458c9b42d9c.
---
 clang/include/clang/Sema/Sema.h | 11 +----------
 clang/lib/Sema/SemaTemplate.cpp |  8 +++-----
 2 files changed, 4 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d3eef97811cbd..b8d760e7e0975 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12198,21 +12198,12 @@ class Sema final : public SemaBase {
   /// If an error occurred, it returns ExprError(); otherwise, it
   /// returns the converted template argument. \p ParamType is the
   /// type of the non-type template parameter after it has been instantiated.
-  ///
-  /// If \p SkipConstantEvaluation is true, the argument is converted to the
-  /// parameter type, but is not required to be constant-evaluable, similarly
-  /// to a value-dependent argument. This is used when only the converted form
-  /// of the argument is needed, e.g. when re-checking the replacement of a
-  /// SubstNonTypeTemplateParmExpr in an unevaluated context, where constant
-  /// evaluation may fail for otherwise valid arguments because the entities
-  /// they refer to are not odr-used.
   ExprResult CheckTemplateArgument(NamedDecl *Param,
                                    QualType InstantiatedParamType, Expr *Arg,
                                    TemplateArgument &SugaredConverted,
                                    TemplateArgument &CanonicalConverted,
                                    bool StrictCheck,
-                                   CheckTemplateArgumentKind CTAK,
-                                   bool SkipConstantEvaluation = false);
+                                   CheckTemplateArgumentKind CTAK);
 
   /// Check a template argument against its corresponding
   /// template template parameter.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 9c53d1501fea1..8c94a1ad39208 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7140,8 +7140,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
                                        TemplateArgument &SugaredConverted,
                                        TemplateArgument &CanonicalConverted,
                                        bool StrictCheck,
-                                       CheckTemplateArgumentKind CTAK,
-                                       bool SkipConstantEvaluation) {
+                                       CheckTemplateArgumentKind CTAK) {
   SourceLocation StartLoc = Arg->getBeginLoc();
   auto *ArgPE = dyn_cast<PackExpansionExpr>(Arg);
   Expr *DeductionArg = ArgPE ? ArgPE->getPattern() : Arg;
@@ -7345,9 +7344,8 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
     }
 
     // For a value-dependent argument, CheckConvertedConstantExpression is
-    // permitted (and expected) to be unable to determine a value. Treat the
-    // argument the same way when the caller does not need the value.
-    if (ArgResult.get()->isValueDependent() || SkipConstantEvaluation) {
+    // permitted (and expected) to be unable to determine a value.
+    if (ArgResult.get()->isValueDependent()) {
       setDeductionArg(ArgResult.get());
       SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
       CanonicalConverted =

>From e67097d09dde399ceb2e5792a9c8a4d7344a8786 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Fri, 5 Jun 2026 11:41:09 +0900
Subject: [PATCH 14/15] insert constant-evaluated context before transforming
 replacement

---
 clang/lib/Sema/TreeTransform.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 70cdc47e42d5d..d56769188b6d7 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16701,6 +16701,19 @@ template <typename Derived>
 ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     SubstNonTypeTemplateParmExpr *E) {
   Expr *OrigReplacement = E->getReplacement()->IgnoreImplicitAsWritten();
+
+  // Insert a constant-evaluated context for the transform.
+  // Otherwise, when a normalized constraint places the replacement inside
+  // an unevaluated operand (e.g. decltype), entities it refers to are not
+  // odr-used, and the constant evaluation performed by CheckTemplateArgument
+  // below can spuriously fail for otherwise valid replacements,
+  // e.g. when a call materializes a function parameter of class type whose
+  // special members were never instantiated.
+  EnterExpressionEvaluationContext ConstantEvaluated(
+      SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
+      /*LambdaContextDecl=*/nullptr,
+      Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument);
+
   ExprResult Replacement = getDerived().TransformExpr(OrigReplacement);
   if (Replacement.isInvalid())
     return true;

>From 59cadb7ce787c6c5fb3a4388b1f3cf9a851007c3 Mon Sep 17 00:00:00 2001
From: I <wx257osn2 at yahoo.co.jp>
Date: Fri, 5 Jun 2026 11:56:44 +0900
Subject: [PATCH 15/15] reuse lambda context decl

---
 clang/lib/Sema/TreeTransform.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index d56769188b6d7..fcc7a932ab194 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16711,7 +16711,7 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
   // special members were never instantiated.
   EnterExpressionEvaluationContext ConstantEvaluated(
       SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated,
-      /*LambdaContextDecl=*/nullptr,
+      Sema::ReuseLambdaContextDecl,
       Sema::ExpressionEvaluationContextRecord::EK_TemplateArgument);
 
   ExprResult Replacement = getDerived().TransformExpr(OrigReplacement);



More information about the cfe-commits mailing list