Skip to content

[Clang] FunctionEffects: Make a separate diagnostic group for redeclarations/overrides where effects are implicit. #148690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,12 @@ Improvements to Clang's diagnostics
pointer, provided it can be proven that the pointer only points to
``[[noreturn]]`` functions.

- Added a separate diagnostic group ``-Wfunction-effect-redeclarations``, for the more pedantic
diagnostics for function effects (``[[clang::nonblocking]]`` and ``[[clang::nonallocating]]``).
Moved the warning for a missing (though implied) attribute on a redeclaration into this group.
Added a new warning in this group for the case where the attribute is missing/implicit on
an override of a virtual method.

Improvements to Clang's time-trace
----------------------------------

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,7 @@ def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">;
// Warnings and notes related to the function effects system which underlies
// the nonblocking and nonallocating attributes.
def FunctionEffects : DiagGroup<"function-effects">;
def FunctionEffectRedeclarations : DiagGroup<"function-effect-redeclarations">;
def PerfConstraintImpliesNoexcept : DiagGroup<"perf-constraint-implies-noexcept">;

// Uniqueness Analysis warnings
Expand Down
23 changes: 17 additions & 6 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -11530,17 +11530,28 @@ def note_in_evaluating_default_argument : Note<
def warn_invalid_add_func_effects : Warning<
"attribute '%0' should not be added via type conversion">,
InGroup<FunctionEffects>, DefaultIgnore;
def warn_mismatched_func_effect_override : Warning<
"attribute '%0' on overriding function does not match base declaration">,
InGroup<FunctionEffects>, DefaultIgnore;
def warn_mismatched_func_effect_redeclaration : Warning<
"attribute '%0' on function does not match previous declaration">,
InGroup<FunctionEffects>, DefaultIgnore;
def warn_conflicting_func_effect_override
: Warning<"attribute '%0' on overriding function conflicts with base "
"declaration">,
InGroup<FunctionEffects>,
DefaultIgnore;
def warn_conflicting_func_effects : Warning<
"effects conflict when merging declarations; kept '%0', discarded '%1'">,
InGroup<FunctionEffects>, DefaultIgnore;
def err_func_with_effects_no_prototype : Error<
"'%0' function must have a prototype">;
// These are more pedantic: in redeclarations and virtual method overrides,
// the effect attribute(s) should be restated.
def warn_mismatched_func_effect_override
: Warning<"overriding function is missing '%0' attribute from base "
"declaration">,
InGroup<FunctionEffectRedeclarations>,
DefaultIgnore;
def warn_mismatched_func_effect_redeclaration
: Warning<
"redeclaration is missing '%0' attribute from previous declaration">,
InGroup<FunctionEffectRedeclarations>,
DefaultIgnore;

} // end of sema category

Expand Down
10 changes: 9 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18682,7 +18682,7 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New,
case FunctionEffectDiff::OverrideResult::NoAction:
break;
case FunctionEffectDiff::OverrideResult::Warn:
Diag(New->getLocation(), diag::warn_mismatched_func_effect_override)
Diag(New->getLocation(), diag::warn_conflicting_func_effect_override)
<< Diff.effectName();
Diag(Old->getLocation(), diag::note_overridden_virtual_function)
<< Old->getReturnTypeSourceRange();
Expand All @@ -18695,6 +18695,14 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New,
QualType ModQT = Context.getFunctionType(NewFT->getReturnType(),
NewFT->getParamTypes(), EPI);
New->setType(ModQT);
if (Errs.empty()) {
// A warning here is somewhat pedantic. Skip this if there was
// already a merge conflict, which is more serious.
Diag(New->getLocation(), diag::warn_mismatched_func_effect_override)
<< Diff.effectName();
Diag(Old->getLocation(), diag::note_overridden_virtual_function)
<< Old->getReturnTypeSourceRange();
}
break;
}
}
Expand Down
22 changes: 14 additions & 8 deletions clang/test/Sema/attr-nonblocking-sema.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify -Wfunction-effects %s
// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c -std=c23 -Wfunction-effects %s
// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify -Wfunction-effects -Wfunction-effect-redeclarations %s
// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c -std=c23 -Wfunction-effects -Wfunction-effect-redeclarations %s

#if !__has_attribute(nonblocking)
#error "the 'nonblocking' attribute is not available"
Expand Down Expand Up @@ -127,29 +127,35 @@ void type_conversions_2()
#endif

// --- VIRTUAL METHODS ---
// Attributes propagate to overridden methods, so no diagnostics except for conflicts.
// Attributes propagate to overridden methods.
// Check this in the syntax tests too.
#ifdef __cplusplus
struct Base {
virtual void f1();
virtual void nonblocking() noexcept [[clang::nonblocking]];
virtual void nonallocating() noexcept [[clang::nonallocating]];
virtual void nonblocking() noexcept [[clang::nonblocking]]; // expected-note {{overridden virtual function is here}}
virtual void nonallocating() noexcept [[clang::nonallocating]]; // expected-note {{overridden virtual function is here}}
virtual void f2() [[clang::nonallocating]]; // expected-note {{previous declaration is here}}
virtual void f3() [[clang::nonblocking]]; // expected-note {{overridden virtual function is here}}
};

struct Derived : public Base {
void f1() [[clang::nonblocking]] override;
void nonblocking() noexcept override;
void nonallocating() noexcept override;
void nonblocking() noexcept override; // expected-warning {{overriding function is missing 'nonblocking' attribute from base declaration}}
void nonallocating() noexcept override; // expected-warning {{overriding function is missing 'nonallocating' attribute from base declaration}}
void f2() [[clang::allocating]] override; // expected-warning {{effects conflict when merging declarations; kept 'allocating', discarded 'nonallocating'}}
};

template <bool B>
struct TDerived : public Base {
void f3() [[clang::nonblocking(B)]] override; // expected-warning {{attribute 'nonblocking' on overriding function conflicts with base declaration}}
};
#endif // __cplusplus

// --- REDECLARATIONS ---

void f2();
void f2() [[clang::nonblocking]]; // expected-note {{previous declaration is here}}
void f2(); // expected-warning {{attribute 'nonblocking' on function does not match previous declaration}}
void f2(); // expected-warning {{redeclaration is missing 'nonblocking' attribute from previous declaration}}
// Note: we verify that the attribute is actually seen during the constraints tests.

void f3() [[clang::blocking]]; // expected-note {{previous declaration is here}}
Expand Down