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
