Skip to content

Commit cfc20ea

Browse files
dougsonosDoug WyattSirraide
authored
[Clang] FunctionEffects: Make a separate diagnostic group for redeclarations/overrides where effects are implicit. (#148690)
The current function effect diagnostics include these behaviors: When you declare a function `nonblocking` (typically in a header) and then omit the attribute on the implementation (or any other redeclaration), Clang warns: attribute 'nonblocking' on function does not match previous declaration. But if a `nonblocking` function is a C++ virtual method, then overrides are implicitly nonblocking; the attribute doesn't need to be explicitly stated. These behaviors are arguably inconsistent -- and also, both, more pedantic than the rest of the function effect diagnostics. This PR accomplishes two things: - Separates the diagnostic on a redeclaration into a new group, `-Wfunction-effect-redeclarations`, so it can be disabled independently. - Adds a second diagnostic to this new group, for the case of an override method missing the attribute. (This helps in a situation where I'm trying to add `nonblocking` via a macro that does other things and I want to know that the macro is missing on an override declaration.) --------- Co-authored-by: Doug Wyatt <[email protected]> Co-authored-by: Sirraide <[email protected]>
1 parent a89e6f6 commit cfc20ea

File tree

5 files changed

+47
-15
lines changed

5 files changed

+47
-15
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,12 @@ Improvements to Clang's diagnostics
710710
pointer, provided it can be proven that the pointer only points to
711711
``[[noreturn]]`` functions.
712712

713+
- Added a separate diagnostic group ``-Wfunction-effect-redeclarations``, for the more pedantic
714+
diagnostics for function effects (``[[clang::nonblocking]]`` and ``[[clang::nonallocating]]``).
715+
Moved the warning for a missing (though implied) attribute on a redeclaration into this group.
716+
Added a new warning in this group for the case where the attribute is missing/implicit on
717+
an override of a virtual method.
718+
713719
Improvements to Clang's time-trace
714720
----------------------------------
715721

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,7 @@ def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">;
12931293
// Warnings and notes related to the function effects system which underlies
12941294
// the nonblocking and nonallocating attributes.
12951295
def FunctionEffects : DiagGroup<"function-effects">;
1296+
def FunctionEffectRedeclarations : DiagGroup<"function-effect-redeclarations">;
12961297
def PerfConstraintImpliesNoexcept : DiagGroup<"perf-constraint-implies-noexcept">;
12971298

12981299
// Uniqueness Analysis warnings

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11530,17 +11530,28 @@ def note_in_evaluating_default_argument : Note<
1153011530
def warn_invalid_add_func_effects : Warning<
1153111531
"attribute '%0' should not be added via type conversion">,
1153211532
InGroup<FunctionEffects>, DefaultIgnore;
11533-
def warn_mismatched_func_effect_override : Warning<
11534-
"attribute '%0' on overriding function does not match base declaration">,
11535-
InGroup<FunctionEffects>, DefaultIgnore;
11536-
def warn_mismatched_func_effect_redeclaration : Warning<
11537-
"attribute '%0' on function does not match previous declaration">,
11538-
InGroup<FunctionEffects>, DefaultIgnore;
11533+
def warn_conflicting_func_effect_override
11534+
: Warning<"attribute '%0' on overriding function conflicts with base "
11535+
"declaration">,
11536+
InGroup<FunctionEffects>,
11537+
DefaultIgnore;
1153911538
def warn_conflicting_func_effects : Warning<
1154011539
"effects conflict when merging declarations; kept '%0', discarded '%1'">,
1154111540
InGroup<FunctionEffects>, DefaultIgnore;
1154211541
def err_func_with_effects_no_prototype : Error<
1154311542
"'%0' function must have a prototype">;
11543+
// These are more pedantic: in redeclarations and virtual method overrides,
11544+
// the effect attribute(s) should be restated.
11545+
def warn_mismatched_func_effect_override
11546+
: Warning<"overriding function is missing '%0' attribute from base "
11547+
"declaration">,
11548+
InGroup<FunctionEffectRedeclarations>,
11549+
DefaultIgnore;
11550+
def warn_mismatched_func_effect_redeclaration
11551+
: Warning<
11552+
"redeclaration is missing '%0' attribute from previous declaration">,
11553+
InGroup<FunctionEffectRedeclarations>,
11554+
DefaultIgnore;
1154411555

1154511556
} // end of sema category
1154611557

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18682,7 +18682,7 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New,
1868218682
case FunctionEffectDiff::OverrideResult::NoAction:
1868318683
break;
1868418684
case FunctionEffectDiff::OverrideResult::Warn:
18685-
Diag(New->getLocation(), diag::warn_mismatched_func_effect_override)
18685+
Diag(New->getLocation(), diag::warn_conflicting_func_effect_override)
1868618686
<< Diff.effectName();
1868718687
Diag(Old->getLocation(), diag::note_overridden_virtual_function)
1868818688
<< Old->getReturnTypeSourceRange();
@@ -18695,6 +18695,14 @@ bool Sema::CheckOverridingFunctionAttributes(CXXMethodDecl *New,
1869518695
QualType ModQT = Context.getFunctionType(NewFT->getReturnType(),
1869618696
NewFT->getParamTypes(), EPI);
1869718697
New->setType(ModQT);
18698+
if (Errs.empty()) {
18699+
// A warning here is somewhat pedantic. Skip this if there was
18700+
// already a merge conflict, which is more serious.
18701+
Diag(New->getLocation(), diag::warn_mismatched_func_effect_override)
18702+
<< Diff.effectName();
18703+
Diag(Old->getLocation(), diag::note_overridden_virtual_function)
18704+
<< Old->getReturnTypeSourceRange();
18705+
}
1869818706
break;
1869918707
}
1870018708
}

clang/test/Sema/attr-nonblocking-sema.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify -Wfunction-effects %s
2-
// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c -std=c23 -Wfunction-effects %s
1+
// RUN: %clang_cc1 -fsyntax-only -fblocks -fcxx-exceptions -verify -Wfunction-effects -Wfunction-effect-redeclarations %s
2+
// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -x c -std=c23 -Wfunction-effects -Wfunction-effect-redeclarations %s
33

44
#if !__has_attribute(nonblocking)
55
#error "the 'nonblocking' attribute is not available"
@@ -127,29 +127,35 @@ void type_conversions_2()
127127
#endif
128128

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

140141
struct Derived : public Base {
141142
void f1() [[clang::nonblocking]] override;
142-
void nonblocking() noexcept override;
143-
void nonallocating() noexcept override;
143+
void nonblocking() noexcept override; // expected-warning {{overriding function is missing 'nonblocking' attribute from base declaration}}
144+
void nonallocating() noexcept override; // expected-warning {{overriding function is missing 'nonallocating' attribute from base declaration}}
144145
void f2() [[clang::allocating]] override; // expected-warning {{effects conflict when merging declarations; kept 'allocating', discarded 'nonallocating'}}
145146
};
147+
148+
template <bool B>
149+
struct TDerived : public Base {
150+
void f3() [[clang::nonblocking(B)]] override; // expected-warning {{attribute 'nonblocking' on overriding function conflicts with base declaration}}
151+
};
146152
#endif // __cplusplus
147153

148154
// --- REDECLARATIONS ---
149155

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

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

0 commit comments

Comments
 (0)