From 087cb2e91c5b276531b617781974e76145b7da0f Mon Sep 17 00:00:00 2001 From: Samarth Narang <70980689+snarang181@users.noreply.github.com> Date: Wed, 23 Jul 2025 21:04:05 -0400 Subject: [PATCH] [clang] Avoid inheriting [[noreturn]] in explicit function template specializations (#150003) This patch fixes incorrect behavior in Clang where [[noreturn]] (either spelled or inferred) was being inherited by explicit specializations of function templates or member function templates, even when those specializations returned normally. Follow up on https://github.com/llvm/llvm-project/pull/145166 (cherry picked from commit 22fef005225b129d73ade4ed995fc0ec0c7be044) --- clang/lib/Sema/SemaDecl.cpp | 8 ++++++++ clang/lib/Sema/SemaDeclAttr.cpp | 7 +++++++ clang/test/SemaCXX/wreturn-always-throws.cpp | 21 +++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 14403e65e8f42..bb412ef6788e7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3267,6 +3267,14 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, if (isa(I) || isa(I)) continue; + if (isa(I)) { + if (auto *FD = dyn_cast(New)) { + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + continue; // Don't propagate inferred noreturn attributes to explicit + // specializations. + } + } + if (mergeDeclAttribute(*this, New, I, LocalAMK)) foundAny = true; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index eff5f9568236a..a7897bdfe6e0f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1970,6 +1970,13 @@ void clang::inferNoReturnAttr(Sema &S, const Decl *D) { if (!FD) return; + // Skip explicit specializations here as they may have + // a user-provided definition that may deliberately differ from the primary + // template. If an explicit specialization truly never returns, the user + // should explicitly mark it with [[noreturn]]. + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return; + auto *NonConstFD = const_cast(FD); DiagnosticsEngine &Diags = S.getDiagnostics(); if (Diags.isIgnored(diag::warn_falloff_nonvoid, FD->getLocation()) && diff --git a/clang/test/SemaCXX/wreturn-always-throws.cpp b/clang/test/SemaCXX/wreturn-always-throws.cpp index addcadd1183dc..df7689f7063cc 100644 --- a/clang/test/SemaCXX/wreturn-always-throws.cpp +++ b/clang/test/SemaCXX/wreturn-always-throws.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -fexceptions -Wreturn-type -verify %s +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -fexceptions -Wreturn-type -Winvalid-noreturn -verify %s // expected-no-diagnostics namespace std { @@ -44,3 +44,22 @@ void testTemplates() { throwErrorTemplate("ERROR"); (void)ensureZeroTemplate(42); } + +// Ensure that explicit specialization of a member function does not inherit +// the warning from the primary template. + +template +struct S { + void f(); + void g(); +}; + +template +void S::f() { throw 0; } +template<> +void S::f() {} + +template +void S::g() {} +template<> +void S::g() { throw 0; }