diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index b1bada65cb6b2..08bb1d81ba29f 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -73,7 +73,7 @@ def warn_pragma_debug_unexpected_argument : Warning< def warn_fp_nan_inf_when_disabled : Warning< "use of %select{infinity|NaN}0%select{| via a macro}1 is undefined behavior " "due to the currently enabled floating-point options">, - InGroup>; + InGroup>; } // Parse && Sema diff --git a/clang/include/clang/Basic/DiagnosticDocs.td b/clang/include/clang/Basic/DiagnosticDocs.td index e9862422b4997..8c024b5cad740 100644 --- a/clang/include/clang/Basic/DiagnosticDocs.td +++ b/clang/include/clang/Basic/DiagnosticDocs.td @@ -87,3 +87,12 @@ program by treating all string literals as having type ``const char *`` instead of ``char *``. This can cause unexpected behaviors with type-sensitive constructs like ``_Generic``. }]; + +defvar NanInfDisabledDocs = [{ +This warning is enabled when source code using the macros ``INFINITY`` or ``NAN`` +is compiled with floating-point options preventing these two values. This can +lead to undefined behavior. Check the order of command line arguments that modify +this behavior, such as ``-ffast-math``, ``-fhonor-infinities``, and +``-fhonor-nans`` (etc), as well as ``#pragma`` directives if this diagnostic is +generated unexpectedly. +}]; diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 9d0d53129a12d..0836b7d439bb0 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -2838,7 +2838,8 @@ class Preprocessor { return AnnotationInfos.find(II)->second; } - void emitMacroExpansionWarnings(const Token &Identifier) const { + void emitMacroExpansionWarnings(const Token &Identifier, + bool IsIfnDef = false) const { IdentifierInfo *Info = Identifier.getIdentifierInfo(); if (Info->isDeprecatedMacro()) emitMacroDeprecationWarning(Identifier); @@ -2847,12 +2848,12 @@ class Preprocessor { !SourceMgr.isInMainFile(Identifier.getLocation())) emitRestrictExpansionWarning(Identifier); - if (Info->getName() == "INFINITY") - if (getLangOpts().NoHonorInfs) + if (!IsIfnDef) { + if (Info->getName() == "INFINITY" && getLangOpts().NoHonorInfs) emitRestrictInfNaNWarning(Identifier, 0); - if (Info->getName() == "NAN") - if (getLangOpts().NoHonorNaNs) + if (Info->getName() == "NAN" && getLangOpts().NoHonorNaNs) emitRestrictInfNaNWarning(Identifier, 1); + } } static void processPathForFileMacro(SmallVectorImpl &Path, diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 9f82a6d073e3b..a980f4bcbae12 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -3288,7 +3288,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result, return; } - emitMacroExpansionWarnings(MacroNameTok); + emitMacroExpansionWarnings(MacroNameTok, /*IsIfnDef=*/true); // Check to see if this is the last token on the #if[n]def line. CheckEndOfDirective(isIfndef ? "ifndef" : "ifdef"); diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp index 1feb0eb18d71e..8f25c67ec9dfb 100644 --- a/clang/lib/Lex/PPExpressions.cpp +++ b/clang/lib/Lex/PPExpressions.cpp @@ -133,7 +133,9 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT, Result.Val.setIsUnsigned(false); // Result is signed intmax_t. DT.IncludedUndefinedIds = !Macro; - PP.emitMacroExpansionWarnings(PeekTok); + PP.emitMacroExpansionWarnings( + PeekTok, + (II->getName() == "INFINITY" || II->getName() == "NAN") ? true : false); // If there is a macro, mark it used. if (Result.Val != 0 && ValueLive) diff --git a/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp b/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp index 8a610fa0e737e..03a432e05851d 100644 --- a/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp +++ b/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp @@ -1,13 +1,31 @@ -// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan -triple powerpc64le-unknown-unknown %s \ -// RUN: -menable-no-infs -menable-no-nans +// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \ +// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \ +// RUN: -menable-no-nans -std=c++23 -// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown %s +// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \ +// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \ +// RUN: -menable-no-nans -funsafe-math-optimizations -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \ +// RUN: %s -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \ +// RUN: -menable-no-infs -std=c++23 // RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \ -// RUN: -menable-no-infs +// RUN: -menable-no-infs -funsafe-math-optimizations -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \ +// RUN: -menable-no-nans -std=c++23 // RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \ -// RUN: -menable-no-nans +// RUN: -funsafe-math-optimizations -menable-no-nans -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \ +// RUN: %s -Wno-nan-infinity-disabled -menable-no-infs -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \ +// RUN: %s -Wno-nan-infinity-disabled -menable-no-nans -std=c++23 // no-fast-no-diagnostics @@ -133,13 +151,41 @@ int compareit(float a, float b) { // no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}} p = __builtin_isfinite(a); - // These should NOT warn, since they are not using NaN or infinity. +// These should NOT warn, since they are not using NaN or infinity. j = a > 1.1; j = b < 1.1; j = a >= 1.1; j = b <= 1.1; j = isunorderedf(a, b); +#ifndef INFINITY + j = a; +#endif +#ifndef NAN + j = b; +#endif +#ifdef INFINITY + j = a; +#endif +#ifdef NAN + j = b; +#endif +#if defined(INFINITY) + j = a; +#elifndef(INFINITY) + j = b; +#endif +#if defined(INFINITY) + j = a; +#elifndef(NAN) + j = b; +#endif +#if defined(NAN) + j = a; +#elifndef(INFINITY) + j = b; +#endif + // no-inf-no-nan-warning@+4 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}} // no-inf-no-nan-warning@+3 {{use of NaN is undefined behavior due to the currently enabled floating-point options}} // no-nan-warning@+2 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}} @@ -173,4 +219,4 @@ int compareit(float a, float b) { j = numeric_limits::infinity(); return 0; -} +} diff --git a/clang/test/Sema/warn-infinity-nan-disabled-win.cpp b/clang/test/Sema/warn-infinity-nan-disabled-win.cpp index 19a575386e329..51f9d325619ba 100644 --- a/clang/test/Sema/warn-infinity-nan-disabled-win.cpp +++ b/clang/test/Sema/warn-infinity-nan-disabled-win.cpp @@ -1,16 +1,34 @@ // Use of NAN macro will trigger a warning "infinity defined in macro" because // on Windows the NAN macro is defined using INFINITY. See below. -// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan -triple powerpc64le-unknown-unknown %s \ -// RUN: -menable-no-infs -menable-no-nans +// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \ +// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \ +// RUN: -menable-no-nans -std=c++23 -// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown %s +// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \ +// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \ +// RUN: -menable-no-nans -funsafe-math-optimizations -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \ +// RUN: %s -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \ +// RUN: -menable-no-infs -std=c++23 // RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \ -// RUN: -menable-no-infs +// RUN: -menable-no-infs -funsafe-math-optimizations -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \ +// RUN: -menable-no-nans -std=c++23 // RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \ -// RUN: -menable-no-nans +// RUN: -funsafe-math-optimizations -menable-no-nans -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \ +// RUN: %s -Wno-nan-infinity-disabled -menable-no-infs -std=c++23 + +// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \ +// RUN: %s -Wno-nan-infinity-disabled -menable-no-nans -std=c++23 // no-fast-no-diagnostics @@ -136,13 +154,41 @@ int compareit(float a, float b) { // no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}} p = __builtin_isfinite(a); - // These should NOT warn, since they are not using NaN or infinity. +// These should NOT warn, since they are not using NaN or infinity. j = a > 1.1; j = b < 1.1; j = a >= 1.1; j = b <= 1.1; j = isunorderedf(a, b); +#ifndef INFINITY + j = a; +#endif +#ifndef NAN + j = b; +#endif +#ifdef INFINITY + j = a; +#endif +#ifdef NAN + j = b; +#endif +#if defined(INFINITY) + j = a; +#elifndef(INFINITY) + j = b; +#endif +#if defined(INFINITY) + j = a; +#elifndef(NAN) + j = b; +#endif +#if defined(NAN) + j = a; +#elifndef(INFINITY) + j = b; +#endif + // no-inf-no-nan-warning@+4 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point option}} // no-inf-no-nan-warning@+3 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}} // no-inf-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}} @@ -176,4 +222,4 @@ int compareit(float a, float b) { j = numeric_limits::infinity(); return 0; -} +}