diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -465,6 +465,8 @@ - Clang now automatically adds ``[[clang::lifetimebound]]`` to the parameters of ``std::move, std::forward`` et al, this enables Clang to diagnose more cases where the returned reference outlives the object. +- Add ``-Wcompare-function-pointers`` to warn about comparisons that may have their behavior + change when enabling the identical code folding optimization feature of some linkers. Non-comprehensive list of changes in this release ------------------------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -565,6 +565,7 @@ def DeprecatedObjCIsaUsage : DiagGroup<"deprecated-objc-isa-usage">; def ExplicitInitializeCall : DiagGroup<"explicit-initialize-call">; def OrderedCompareFunctionPointers : DiagGroup<"ordered-compare-function-pointers">; +def CompareFunctionPointers : DiagGroup<"compare-function-pointers", [OrderedCompareFunctionPointers]>; def PackedNonPod : DiagGroup<"packed-non-pod">; def Packed : DiagGroup<"packed", [PackedNonPod]>; def Padded : DiagGroup<"padded">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7010,6 +7010,10 @@ "%0 is %select{|in}2complete and " "%1 is %select{|in}3complete">, InGroup; +def warn_typecheck_comparison_of_function_pointers : Warning< + "distinct function pointers (%0 and %1) may compare equal " + "when using identical code folding">, + InGroup, DefaultIgnore; def warn_typecheck_ordered_comparison_of_function_pointers : Warning< "ordered comparison of function pointers (%0 and %1)">, InGroup; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -12714,6 +12714,13 @@ LHS.get()->getSourceRange()); } + if (!IsOrdered && LHSType->isFunctionPointerType() && + RHSType->isFunctionPointerType() && !LHSIsNull && !RHSIsNull && + RHSType == LHSType) + Diag(Loc, diag::warn_typecheck_comparison_of_function_pointers) + << LHSType << RHSType + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + if (IsOrdered && LHSType->isFunctionPointerType() && RHSType->isFunctionPointerType()) { // Valid unless a relational comparison of function pointers diff --git a/clang/test/SemaCXX/compare-function-pointer.cpp b/clang/test/SemaCXX/compare-function-pointer.cpp --- a/clang/test/SemaCXX/compare-function-pointer.cpp +++ b/clang/test/SemaCXX/compare-function-pointer.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify -Wcompare-function-pointers %s using fp0_t = void (*)(); using fp1_t = int (*)(); @@ -6,8 +6,8 @@ extern fp0_t a, b; extern fp1_t c; -bool eq0 = a == b; -bool ne0 = a != b; +bool eq0 = a == b; // expected-warning {{distinct function pointers ('fp0_t' (aka 'void (*)()') and 'fp0_t')}} +bool ne0 = a != b; // expected-warning {{distinct function pointers}} bool lt0 = a < b; // expected-warning {{ordered comparison of function pointers ('fp0_t' (aka 'void (*)()') and 'fp0_t')}} bool le0 = a <= b; // expected-warning {{ordered comparison of function pointers}} bool gt0 = a > b; // expected-warning {{ordered comparison of function pointers}}