https://github.com/flash1729 created 
https://github.com/llvm/llvm-project/pull/234168

`ShouldRemoveFromUnused` only counts a function or variable template as used 
when some specialization is odr-used or has had its definition instantiated. A 
specialization that is implicitly instantiated because it was named in an 
unevaluated operand (decltype, sizeof, noexcept, a requires-expression) is only 
marked referenced and never gets a body, so the template was diagnosed as 
unused even though it determined the meaning of that expression.

Treat a referenced implicit specialization as a use of its template.

Fixes #218429

>From b683d93cd0843a65c985f0845c9c5281adb4b323 Mon Sep 17 00:00:00 2001
From: flash1729 <[email protected]>
Date: Wed, 14 Oct 2026 17:09:04 +0530
Subject: [PATCH] [clang] Treat templates referenced in unevaluated operands as
 used for -Wunused-template

ShouldRemoveFromUnused only counts a function or variable template as used
when some specialization is odr-used or has had its definition
instantiated. A specialization that is implicitly instantiated because it
was named in an unevaluated operand (decltype, sizeof, noexcept, a
requires-expression) is only marked referenced and never gets a body, so
the template was diagnosed as unused even though it determined the meaning
of that expression.

Treat a referenced implicit specialization as a use of its template.

Fixes #218429
---
 clang/docs/ReleaseNotes.md                    |  4 +++
 clang/lib/Sema/Sema.cpp                       | 19 +++++++++---
 clang/test/SemaCXX/warn-unused-filescoped.cpp | 30 ++++++++++++++++++-
 3 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index e6b71ba2a0b565..f8c8713fce8c4d 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -422,6 +422,10 @@ features cannot lower the translation-unit ABI level;
 - Fixed spurious `-Wimplicit-void-ptr-cast` warnings in C for parenthesized
   null pointer macros such as `(NULL)`. (#GH171874)
 
+- `-Wunused-template` no longer warns about a function or variable template
+  whose only uses are in unevaluated operands such as `decltype`, `sizeof`,
+  `noexcept` or a requires-expression. (#GH218429)
+
 - `-Wfortify-source` now diagnoses when `strlcat`, `__builtin_strlcat`, 
`strlcpy`, or
   `__builtin_strlcpy` is called with a size argument larger than the 
destination buffer.
 
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 9abab22e3809f3..b798ac73b19ade 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -908,10 +908,17 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const 
DeclaratorDecl *D) {
 
   if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
     // If this is a function template and none of its specializations is used,
-    // we should warn.
+    // we should warn. A specialization that was implicitly instantiated only
+    // because it was named in an unevaluated operand (decltype, sizeof,
+    // noexcept, a requires-expression) is referenced but never odr-used and
+    // never gets a body; the template still determined the meaning of that
+    // expression, so treat it as a use.
     if (FunctionTemplateDecl *Template = FD->getDescribedFunctionTemplate())
       for (const auto *Spec : Template->specializations())
-        if (ShouldRemoveFromUnused(SemaRef, Spec))
+        if (ShouldRemoveFromUnused(SemaRef, Spec) ||
+            (Spec->getTemplateSpecializationKind() ==
+                 TSK_ImplicitInstantiation &&
+             Spec->isReferenced()))
           return true;
 
     // UnusedFileScopedDecls stores the first declaration.
@@ -939,9 +946,13 @@ static bool ShouldRemoveFromUnused(Sema *SemaRef, const 
DeclaratorDecl *D) {
 
     if (VarTemplateDecl *Template = VD->getDescribedVarTemplate())
       // If this is a variable template and none of its specializations is 
used,
-      // we should warn.
+      // we should warn. As for function templates above, a specialization that
+      // is only referenced from an unevaluated operand counts as a use.
       for (const auto *Spec : Template->specializations())
-        if (ShouldRemoveFromUnused(SemaRef, Spec))
+        if (ShouldRemoveFromUnused(SemaRef, Spec) ||
+            (Spec->getTemplateSpecializationKind() ==
+                 TSK_ImplicitInstantiation &&
+             Spec->isReferenced()))
           return true;
 
     // UnusedFileScopedDecls stores the first declaration.
diff --git a/clang/test/SemaCXX/warn-unused-filescoped.cpp 
b/clang/test/SemaCXX/warn-unused-filescoped.cpp
index be8d350855c078..3a86528d134d5c 100644
--- a/clang/test/SemaCXX/warn-unused-filescoped.cpp
+++ b/clang/test/SemaCXX/warn-unused-filescoped.cpp
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wunused -Wunused-template 
-Wunused-member-function -Wno-unused-local-typedefs \
 // RUN:            -Wno-c++11-extensions -Wno-c++14-extensions -std=c++98 %s
 // RUN: %clang_cc1 -fsyntax-only -verify -Wunused -Wunused-template 
-Wunused-member-function -Wno-unused-local-typedefs -std=c++14 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wunused -Wunused-template 
-Wunused-member-function -Wno-unused-local-typedefs -std=c++20 %s
 
 #ifdef HEADER
 
@@ -131,7 +132,9 @@ namespace test4 {
 
 namespace rdar8733476 {
 static void foo() {}                         // expected-warning {{function 
'foo' is not needed and will not be emitted}}
-template <typename T> static void foo_t() {} // expected-warning {{unused 
function template 'foo_t'}}
+// foo_t<void> below is referenced from the never-instantiated bar<>, which
+// counts as a use of the template (see GH218429 at the end of this file).
+template <typename T> static void foo_t() {}
 template <> void foo_t<int>() {}             // expected-warning {{function 
'foo_t<int>' is not needed and will not be emitted}}
 
 template <int>
@@ -236,4 +239,29 @@ constexpr int constexpr4() { return 2; }
 #endif
 }
 
+namespace GH218429 {
+// A template whose only uses are in unevaluated operands is still a use: the
+// template determined the meaning of the expression even though no
+// specialization was odr-used or had its definition instantiated.
+#if __cplusplus >= 201103L
+template <typename T> static T in_decltype(T t) { return t; }
+using A = decltype(in_decltype(1));
+template <typename T> static T in_sizeof(T t) { return t; }
+static_assert(sizeof(in_sizeof(1)) == sizeof(int), "");
+template <typename T> static T in_noexcept(T t) { return t; }
+static_assert(!noexcept(in_noexcept(1)), "");
+template <typename T> static T never_named(T t) { return t; } // 
expected-warning {{unused function template 'never_named'}}
+#endif
+#if __cplusplus >= 201402L
+template <typename T> static T var_in_decltype = T{};
+using D = decltype(var_in_decltype<int>);
+template <typename T> static T var_never_named = T{}; // expected-warning 
{{unused variable template 'var_never_named'}}
+#endif
+#if __cplusplus >= 202002L
+template <typename T> static void in_requires(T) {}
+template <typename T> static constexpr bool satisfied = requires { 
in_requires(T{}); };
+static_assert(satisfied<int>);
+#endif
+}
+
 #endif

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to