Skip to content

Commit 1e2a4cc

Browse files
authored
[CLANG] Add warning when INF or NAN are used in a binary operation or as function argument in fast math mode. (#76873)
Check for operations using INF or NaN when in ffast-math mode and generate a warning.
1 parent 0dd72eb commit 1e2a4cc

File tree

8 files changed

+425
-8
lines changed

8 files changed

+425
-8
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,11 @@ Improvements to Clang's diagnostics
596596
- Clang now diagnoses narrowing conversions involving const references.
597597
(`#63151: <https://github.com/llvm/llvm-project/issues/63151>`_).
598598
- Clang now diagnoses unexpanded packs within the template argument lists of function template specializations.
599+
- The warning `-Wnan-infinity-disabled` is now emitted when ``INFINITY``
600+
or ``NAN`` are used in arithmetic operations or function arguments in
601+
floating-point mode where ``INFINITY`` or ``NAN`` don't have the expected
602+
values.
603+
599604
- Clang now diagnoses attempts to bind a bitfield to an NTTP of a reference type as erroneous
600605
converted constant expression and not as a reference to subobject.
601606
- Clang now diagnoses ``auto`` and ``decltype(auto)`` in declarations of conversion function template

clang/include/clang/Basic/DiagnosticCommonKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ def warn_pragma_debug_missing_argument : Warning<
7070
def warn_pragma_debug_unexpected_argument : Warning<
7171
"unexpected argument to debug command">, InGroup<IgnoredPragmas>;
7272

73+
def warn_fp_nan_inf_when_disabled : Warning<
74+
"use of %select{infinity|NaN}0%select{| via a macro}1 is undefined behavior "
75+
"due to the currently enabled floating-point options">,
76+
InGroup<DiagGroup<"nan-infinity-disabled">>;
7377
}
7478

7579
// Parse && Sema

clang/include/clang/Lex/Preprocessor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2835,6 +2835,13 @@ class Preprocessor {
28352835
if (Identifier.getIdentifierInfo()->isRestrictExpansion() &&
28362836
!SourceMgr.isInMainFile(Identifier.getLocation()))
28372837
emitRestrictExpansionWarning(Identifier);
2838+
2839+
if (Identifier.getIdentifierInfo()->getName() == "INFINITY")
2840+
if (getLangOpts().NoHonorInfs)
2841+
emitRestrictInfNaNWarning(Identifier, 0);
2842+
if (Identifier.getIdentifierInfo()->getName() == "NAN")
2843+
if (getLangOpts().NoHonorNaNs)
2844+
emitRestrictInfNaNWarning(Identifier, 1);
28382845
}
28392846

28402847
static void processPathForFileMacro(SmallVectorImpl<char> &Path,
@@ -2850,6 +2857,8 @@ class Preprocessor {
28502857
void emitMacroDeprecationWarning(const Token &Identifier) const;
28512858
void emitRestrictExpansionWarning(const Token &Identifier) const;
28522859
void emitFinalMacroWarning(const Token &Identifier, bool IsUndef) const;
2860+
void emitRestrictInfNaNWarning(const Token &Identifier,
2861+
unsigned DiagSelection) const;
28532862

28542863
/// This boolean state keeps track if the current scanned token (by this PP)
28552864
/// is in an "-Wunsafe-buffer-usage" opt-out region. Assuming PP scans a

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13932,8 +13932,9 @@ class Sema final {
1393213932

1393313933
bool SemaBuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall);
1393413934
bool SemaBuiltinVAStartARMMicrosoft(CallExpr *Call);
13935-
bool SemaBuiltinUnorderedCompare(CallExpr *TheCall);
13936-
bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs);
13935+
bool SemaBuiltinUnorderedCompare(CallExpr *TheCall, unsigned BuiltinID);
13936+
bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs,
13937+
unsigned BuiltinID);
1393713938
bool SemaBuiltinComplex(CallExpr *TheCall);
1393813939
bool SemaBuiltinVSX(CallExpr *TheCall);
1393913940
bool SemaBuiltinOSLogFormat(CallExpr *TheCall);
@@ -14037,6 +14038,8 @@ class Sema final {
1403714038
SourceRange range,
1403814039
llvm::SmallBitVector &CheckedVarArgs);
1403914040

14041+
void CheckInfNaNFunction(const CallExpr *Call, const FunctionDecl *FDecl);
14042+
1404014043
void CheckAbsoluteValueFunction(const CallExpr *Call,
1404114044
const FunctionDecl *FDecl);
1404214045

clang/lib/Lex/Preprocessor.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,11 @@ void Preprocessor::emitRestrictExpansionWarning(const Token &Identifier) const {
14571457
Diag(Info.Location, diag::note_pp_macro_annotation) << 1;
14581458
}
14591459

1460+
void Preprocessor::emitRestrictInfNaNWarning(const Token &Identifier,
1461+
unsigned DiagSelection) const {
1462+
Diag(Identifier, diag::warn_fp_nan_inf_when_disabled) << DiagSelection << 1;
1463+
}
1464+
14601465
void Preprocessor::emitFinalMacroWarning(const Token &Identifier,
14611466
bool IsUndef) const {
14621467
const MacroAnnotations &A =

clang/lib/Sema/SemaChecking.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,6 +2169,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
21692169
ICEArguments &= ~(1 << ArgNo);
21702170
}
21712171

2172+
FPOptions FPO;
21722173
switch (BuiltinID) {
21732174
case Builtin::BI__builtin___CFStringMakeConstantString:
21742175
// CFStringMakeConstantString is currently not implemented for GOFF (i.e.,
@@ -2245,15 +2246,15 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22452246
case Builtin::BI__builtin_islessequal:
22462247
case Builtin::BI__builtin_islessgreater:
22472248
case Builtin::BI__builtin_isunordered:
2248-
if (SemaBuiltinUnorderedCompare(TheCall))
2249+
if (SemaBuiltinUnorderedCompare(TheCall, BuiltinID))
22492250
return ExprError();
22502251
break;
22512252
case Builtin::BI__builtin_fpclassify:
2252-
if (SemaBuiltinFPClassification(TheCall, 6))
2253+
if (SemaBuiltinFPClassification(TheCall, 6, BuiltinID))
22532254
return ExprError();
22542255
break;
22552256
case Builtin::BI__builtin_isfpclass:
2256-
if (SemaBuiltinFPClassification(TheCall, 2))
2257+
if (SemaBuiltinFPClassification(TheCall, 2, BuiltinID))
22572258
return ExprError();
22582259
break;
22592260
case Builtin::BI__builtin_isfinite:
@@ -2267,7 +2268,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22672268
case Builtin::BI__builtin_signbit:
22682269
case Builtin::BI__builtin_signbitf:
22692270
case Builtin::BI__builtin_signbitl:
2270-
if (SemaBuiltinFPClassification(TheCall, 1))
2271+
if (SemaBuiltinFPClassification(TheCall, 1, BuiltinID))
22712272
return ExprError();
22722273
break;
22732274
case Builtin::BI__builtin_shufflevector:
@@ -7648,6 +7649,7 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
76487649

76497650
CheckAbsoluteValueFunction(TheCall, FDecl);
76507651
CheckMaxUnsignedZero(TheCall, FDecl);
7652+
CheckInfNaNFunction(TheCall, FDecl);
76517653

76527654
if (getLangOpts().ObjC)
76537655
DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs);
@@ -9117,10 +9119,15 @@ bool Sema::SemaBuiltinVAStartARMMicrosoft(CallExpr *Call) {
91179119

91189120
/// SemaBuiltinUnorderedCompare - Handle functions like __builtin_isgreater and
91199121
/// friends. This is declared to take (...), so we have to check everything.
9120-
bool Sema::SemaBuiltinUnorderedCompare(CallExpr *TheCall) {
9122+
bool Sema::SemaBuiltinUnorderedCompare(CallExpr *TheCall, unsigned BuiltinID) {
91219123
if (checkArgCount(*this, TheCall, 2))
91229124
return true;
91239125

9126+
if (BuiltinID == Builtin::BI__builtin_isunordered &&
9127+
TheCall->getFPFeaturesInEffect(getLangOpts()).getNoHonorNaNs())
9128+
Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
9129+
<< 1 << 0 << TheCall->getSourceRange();
9130+
91249131
ExprResult OrigArg0 = TheCall->getArg(0);
91259132
ExprResult OrigArg1 = TheCall->getArg(1);
91269133

@@ -9155,10 +9162,23 @@ bool Sema::SemaBuiltinUnorderedCompare(CallExpr *TheCall) {
91559162
/// SemaBuiltinSemaBuiltinFPClassification - Handle functions like
91569163
/// __builtin_isnan and friends. This is declared to take (...), so we have
91579164
/// to check everything.
9158-
bool Sema::SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs) {
9165+
bool Sema::SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs,
9166+
unsigned BuiltinID) {
91599167
if (checkArgCount(*this, TheCall, NumArgs))
91609168
return true;
91619169

9170+
FPOptions FPO = TheCall->getFPFeaturesInEffect(getLangOpts());
9171+
if (FPO.getNoHonorInfs() && (BuiltinID == Builtin::BI__builtin_isfinite ||
9172+
BuiltinID == Builtin::BI__builtin_isinf ||
9173+
BuiltinID == Builtin::BI__builtin_isinf_sign))
9174+
Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
9175+
<< 0 << 0 << TheCall->getSourceRange();
9176+
9177+
if (FPO.getNoHonorNaNs() && (BuiltinID == Builtin::BI__builtin_isnan ||
9178+
BuiltinID == Builtin::BI__builtin_isunordered))
9179+
Diag(TheCall->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
9180+
<< 1 << 0 << TheCall->getSourceRange();
9181+
91629182
bool IsFPClass = NumArgs == 2;
91639183

91649184
// Find out position of floating-point argument.
@@ -12905,6 +12925,22 @@ static bool IsStdFunction(const FunctionDecl *FDecl,
1290512925
return true;
1290612926
}
1290712927

12928+
void Sema::CheckInfNaNFunction(const CallExpr *Call,
12929+
const FunctionDecl *FDecl) {
12930+
FPOptions FPO = Call->getFPFeaturesInEffect(getLangOpts());
12931+
if ((IsStdFunction(FDecl, "isnan") || IsStdFunction(FDecl, "isunordered") ||
12932+
(Call->getBuiltinCallee() == Builtin::BI__builtin_nanf)) &&
12933+
FPO.getNoHonorNaNs())
12934+
Diag(Call->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
12935+
<< 1 << 0 << Call->getSourceRange();
12936+
else if ((IsStdFunction(FDecl, "isinf") ||
12937+
(IsStdFunction(FDecl, "isfinite") ||
12938+
(FDecl->getIdentifier() && FDecl->getName() == "infinity"))) &&
12939+
FPO.getNoHonorInfs())
12940+
Diag(Call->getBeginLoc(), diag::warn_fp_nan_inf_when_disabled)
12941+
<< 0 << 0 << Call->getSourceRange();
12942+
}
12943+
1290812944
// Warn when using the wrong abs() function.
1290912945
void Sema::CheckAbsoluteValueFunction(const CallExpr *Call,
1291012946
const FunctionDecl *FDecl) {
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan -triple powerpc64le-unknown-unknown %s \
2+
// RUN: -menable-no-infs -menable-no-nans
3+
4+
// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown %s
5+
6+
// RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \
7+
// RUN: -menable-no-infs
8+
9+
// RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \
10+
// RUN: -menable-no-nans
11+
12+
// no-fast-no-diagnostics
13+
14+
int isunorderedf (float x, float y);
15+
extern "C++" {
16+
namespace std __attribute__((__visibility__("default"))) {
17+
bool
18+
isinf(float __x);
19+
bool
20+
isinf(double __x);
21+
bool
22+
isinf(long double __x);
23+
bool
24+
isnan(float __x);
25+
bool
26+
isnan(double __x);
27+
bool
28+
isnan(long double __x);
29+
bool
30+
isfinite(float __x);
31+
bool
32+
isfinite(double __x);
33+
bool
34+
isfinte(long double __x);
35+
bool
36+
isunordered(float __x, float __y);
37+
bool
38+
isunordered(double __x, double __y);
39+
bool
40+
isunordered(long double __x, long double __y);
41+
} // namespace )
42+
}
43+
44+
#define NAN (__builtin_nanf(""))
45+
#define INFINITY (__builtin_inff())
46+
47+
template <class _Ty>
48+
class numeric_limits {
49+
public:
50+
[[nodiscard]] static constexpr _Ty infinity() noexcept {
51+
return _Ty();
52+
}
53+
};
54+
55+
template <>
56+
class numeric_limits<float> {
57+
public:
58+
[[nodiscard]] static constexpr float infinity() noexcept {
59+
return __builtin_huge_val();
60+
}
61+
};
62+
template <>
63+
class numeric_limits<double> {
64+
public:
65+
[[nodiscard]] static constexpr double infinity() noexcept {
66+
return __builtin_huge_val();
67+
}
68+
};
69+
70+
int compareit(float a, float b) {
71+
volatile int i, j, k, l, m, n, o, p;
72+
// no-inf-no-nan-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
73+
// no-inf-warning@+1 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
74+
i = a == INFINITY;
75+
76+
// no-inf-no-nan-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
77+
// no-inf-warning@+1 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
78+
j = INFINITY == a;
79+
80+
// no-inf-no-nan-warning@+4 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
81+
// no-inf-no-nan-warning@+3 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
82+
// no-nan-warning@+2 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
83+
// no-nan-warning@+1 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
84+
i = a == NAN;
85+
86+
// no-inf-no-nan-warning@+4 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
87+
// no-inf-no-nan-warning@+3 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
88+
// no-nan-warning@+2 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
89+
// no-nan-warning@+1 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
90+
j = NAN == a;
91+
92+
// no-inf-no-nan-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
93+
// no-inf-warning@+1 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
94+
j = INFINITY <= a;
95+
96+
// no-inf-no-nan-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
97+
// no-inf-warning@+1 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
98+
j = INFINITY < a;
99+
100+
// no-inf-no-nan-warning@+4 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
101+
// no-inf-no-nan-warning@+3 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
102+
// no-nan-warning@+2 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
103+
// no-nan-warning@+1 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
104+
j = a > NAN;
105+
106+
// no-inf-no-nan-warning@+4 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
107+
// no-inf-no-nan-warning@+3 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
108+
// no-nan-warning@+2 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
109+
// no-nan-warning@+1 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
110+
j = a >= NAN;
111+
112+
// no-inf-no-nan-warning@+2 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
113+
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
114+
k = std::isinf(a);
115+
116+
// no-inf-no-nan-warning@+2 {{use of NaN is undefined behavior due to the currently enabled floating-point option}}
117+
// no-nan-warning@+1 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
118+
l = std::isnan(a);
119+
120+
// no-inf-no-nan-warning@+2 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
121+
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
122+
o = std::isfinite(a);
123+
124+
// no-inf-no-nan-warning@+2 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
125+
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
126+
m = __builtin_isinf(a);
127+
128+
// no-inf-no-nan-warning@+2 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
129+
// no-nan-warning@+1 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
130+
n = __builtin_isnan(a);
131+
132+
// no-inf-no-nan-warning@+2 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
133+
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
134+
p = __builtin_isfinite(a);
135+
136+
// These should NOT warn, since they are not using NaN or infinity.
137+
j = a > 1.1;
138+
j = b < 1.1;
139+
j = a >= 1.1;
140+
j = b <= 1.1;
141+
j = isunorderedf(a, b);
142+
143+
// no-inf-no-nan-warning@+4 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
144+
// no-inf-no-nan-warning@+3 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
145+
// no-nan-warning@+2 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
146+
// no-nan-warning@+1 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
147+
j = isunorderedf(a, NAN);
148+
149+
// no-inf-no-nan-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
150+
// no-inf-warning@+1 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
151+
j = isunorderedf(a, INFINITY);
152+
153+
// no-inf-no-nan-warning@+6 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
154+
// no-inf-no-nan-warning@+5 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
155+
// no-inf-no-nan-warning@+4 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
156+
// no-nan-warning@+3 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
157+
// no-nan-warning@+2 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
158+
// no-nan-warning@+1 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
159+
i = std::isunordered(a, NAN);
160+
161+
// no-inf-no-nan-warning@+4 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
162+
// no-inf-no-nan-warning@+3 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
163+
// no-inf-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
164+
// no-nan-warning@+1 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
165+
i = std::isunordered(a, INFINITY);
166+
167+
// no-inf-no-nan-warning@+2 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
168+
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
169+
double y = i * numeric_limits<double>::infinity();
170+
171+
// no-inf-no-nan-warning@+2 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
172+
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
173+
j = numeric_limits<float>::infinity();
174+
return 0;
175+
176+
}

0 commit comments

Comments
 (0)