Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -707,6 +707,12 @@ Improvements to Clang's diagnostics
- Improve the diagnostics for placement new expression when const-qualified
object was passed as the storage argument. (#GH143708)

- 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 @@ -1291,6 +1291,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 @@ -11529,17 +11529,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 @@ -18686,7 +18686,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 @@ -18699,6 +18699,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