[clang] [Clang] Tolerate constant evaluation failure when transforming SubstNonTypeTemplateParmExpr in unevaluated contexts (PR #196791)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 3 04:18:00 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/10] 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 b427e6a4d2fa51..4275f639896078 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/10] 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 70cdc47e42d5d7..34b0ac9e86be55 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/10] 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 9e4a47a5b18fca..a654a94adceb47 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/10] 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 4275f639896078..794229cf0b1db0 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/10] 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 34b0ac9e86be55..8c661ce27fc4c5 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/10] 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 8c661ce27fc4c5..34b0ac9e86be55 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/10] 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 34b0ac9e86be55..70cdc47e42d5d7 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/10] 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 794229cf0b1db0..f735bfc1251c21 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/10] 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 b8d760e7e0975a..d3eef97811cbdf 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 8c94a1ad392086..9c53d1501fea17 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/10] 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 70cdc47e42d5d7..c3e3bdcad14fef 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 {
More information about the cfe-commits
mailing list