[clang] Add clang warning if fp exception functions are called without appropriate flags/pragmas (#128239) (PR #187860)
Marcos Ramirez Joos via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 02:09:11 PDT 2026
https://github.com/maarcosrmz updated https://github.com/llvm/llvm-project/pull/187860
>From 67c5c319f0ba67f5cf1bf664ff9583ce37437956 Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Wed, 11 Mar 2026 21:55:35 +0100
Subject: [PATCH 01/10] Added warning for feclearexcept, feraiseexcept,
fetestexcept, fegetround, and fesetround being used without appropriate
flags/pragmas (#128239)
---
clang/include/clang/Basic/BuiltinHeaders.def | 1 +
clang/include/clang/Basic/Builtins.td | 25 +++++++++++++++++++
.../clang/Basic/DiagnosticSemaKinds.td | 5 ++++
clang/lib/Sema/SemaChecking.cpp | 11 ++++++++
4 files changed, 42 insertions(+)
diff --git a/clang/include/clang/Basic/BuiltinHeaders.def b/clang/include/clang/Basic/BuiltinHeaders.def
index bca40c0e4deea..a375b71f6bce6 100644
--- a/clang/include/clang/Basic/BuiltinHeaders.def
+++ b/clang/include/clang/Basic/BuiltinHeaders.def
@@ -17,6 +17,7 @@ HEADER(BLOCKS_H, "Blocks.h")
HEADER(COMPLEX_H, "complex.h")
HEADER(CTYPE_H, "ctype.h")
HEADER(EMMINTRIN_H, "emmintrin.h")
+HEADER(FENV_H, "fenv.h")
HEADER(FOUNDATION_NSOBJCRUNTIME_H, "Foundation/NSObjCRuntime.h")
HEADER(IMMINTRIN_H, "immintrin.h")
HEADER(INTRIN_H, "intrin.h")
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index f1743c7286def..510241f03102b 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4409,6 +4409,31 @@ def BlockObjectDispose : LibBuiltin<"blocks.h"> {
}
// FIXME: Also declare NSConcreteGlobalBlock and NSConcreteStackBlock.
+def FeClearExcept : LibBuiltin<"fenv.h"> {
+ let Spellings = ["feclearexcept"];
+ let Prototype = "int(int)";
+}
+
+def FeRaiseExcept : LibBuiltin<"fenv.h"> {
+ let Spellings = ["feraiseexcept"];
+ let Prototype = "int(int)";
+}
+
+def FeTestExcept : LibBuiltin<"fenv.h"> {
+ let Spellings = ["fetestexcept"];
+ let Prototype = "int(int)";
+}
+
+def FeGetRound : LibBuiltin<"fenv.h"> {
+ let Spellings = ["fegetround"];
+ let Prototype = "int()";
+}
+
+def FeSetRound : LibBuiltin<"fenv.h"> {
+ let Spellings = ["fesetround"];
+ let Prototype = "int(int)";
+}
+
def __Addressof : LangBuiltin<"CXX_LANG"> {
let Spellings = ["__addressof"];
let Attributes = [FunctionWithoutBuiltinPrefix, NoThrow, Const,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4d352f1def04b..71af7b5e18fe1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1035,6 +1035,11 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
"%select{subtraction|addition}0 of address-of-label expressions is not "
"supported with ptrauth indirect gotos">;
+def warn_fe_access_without_fenv_access : Warning<
+ "'%0' used without enabling floating-point exception behavior; use 'pragma STDC "
+ "FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'">,
+ InGroup<DiagGroup<"fenv-access">>;
+
// __ptrauth qualifier
def err_ptrauth_qualifier_invalid : Error<
"%select{return type|parameter type|property}1 may not be qualified with "
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index de8b965144971..7f80880d83273 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3843,6 +3843,17 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
if (BuiltinCountedByRef(TheCall))
return ExprError();
break;
+
+ case Builtin::BIfeclearexcept:
+ case Builtin::BIferaiseexcept:
+ case Builtin::BIfetestexcept:
+ case Builtin::BIfegetround:
+ case Builtin::BIfesetround:
+ if (TheCall->getFPFeaturesInEffect(getLangOpts()).getExceptionMode() ==
+ LangOptions::FPE_Ignore) {
+ Diag(TheCall->getBeginLoc(), diag::warn_fe_access_without_fenv_access)
+ << FDecl->getName() << TheCall->getSourceRange();
+ }
}
if (getLangOpts().HLSL && HLSL().CheckBuiltinFunctionCall(BuiltinID, TheCall))
>From e8d5e58b8f8576a28691377d93e586ff08ccef67 Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Fri, 27 Mar 2026 20:10:25 +0100
Subject: [PATCH 02/10] Added regression tests for fenv access warning
---
clang/test/Sema/fenv-access.c | 37 +++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 clang/test/Sema/fenv-access.c
diff --git a/clang/test/Sema/fenv-access.c b/clang/test/Sema/fenv-access.c
new file mode 100644
index 0000000000000..57d4debfa9c73
--- /dev/null
+++ b/clang/test/Sema/fenv-access.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -verify -Wfenv-access %s
+// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=maytrap -DNO_WARN %s
+// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=strict -DNO_WARN %s
+
+int feclearexcept(int excepts);
+int feraiseexcept(int excepts);
+int fetestexcept(int excepts);
+int fegetround(void);
+int fesetround(int rounding_mode);
+
+#define FE_INVALID 1
+
+void test_fenv_access_off(void) {
+#ifdef NO_WARN
+ // expected-no-diagnostics
+ feclearexcept(FE_INVALID);
+ feraiseexcept(FE_INVALID);
+ fetestexcept(FE_INVALID);
+ fegetround();
+ fesetround(0);
+#else
+ feclearexcept(FE_INVALID); // expected-warning {{'feclearexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ feraiseexcept(FE_INVALID); // expected-warning {{'feraiseexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ fetestexcept(FE_INVALID); // expected-warning {{'fetestexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ fegetround(); // expected-warning {{'fegetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ fesetround(0); // expected-warning {{'fesetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+#endif
+}
+
+void test_fenv_access_on(void) {
+ #pragma STDC FENV_ACCESS ON
+ feclearexcept(FE_INVALID);
+ feraiseexcept(FE_INVALID);
+ fetestexcept(FE_INVALID);
+ fegetround();
+ fesetround(0);
+}
>From d2dcbe03c3fe686ba01cd5a3a0e398eae3fd4607 Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Sun, 29 Mar 2026 13:21:47 +0200
Subject: [PATCH 03/10] Added builtin types fexcept_t and fenv_t (fenv.h)
---
clang/include/clang/AST/ASTContext.h | 35 +++++++++++++++++-
clang/include/clang/Basic/TokenKinds.def | 2 ++
.../include/clang/Serialization/ASTBitCodes.h | 8 ++++-
clang/lib/AST/ASTContext.cpp | 19 ++++++++++
clang/lib/Sema/SemaDecl.cpp | 8 +++++
clang/lib/Serialization/ASTReader.cpp | 36 +++++++++++++++++++
clang/lib/Serialization/ASTWriter.cpp | 2 ++
clang/utils/TableGen/ClangBuiltinsEmitter.cpp | 2 ++
8 files changed, 110 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index ba1b58489c327..2e42deaa60d5f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -497,6 +497,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// The type for the C ucontext_t type.
TypeDecl *ucontext_tDecl = nullptr;
+ /// The type for the C fexcept_t type.
+ TypeDecl *fexcept_tDecl = nullptr;
+
+ /// The type for the C fenv_t type.
+ TypeDecl *fenv_tDecl = nullptr;
+
/// Type for the Block descriptor for Blocks CodeGen.
///
/// Since this is only used for generation of debug info, it is not
@@ -2334,6 +2340,30 @@ class ASTContext : public RefCountedBase<ASTContext> {
return QualType();
}
+ /// Set the type for the C fexcept_t type.
+ void setfexcept_tDecl(TypeDecl *fexcept_tDecl) {
+ this->fexcept_tDecl = fexcept_tDecl;
+ }
+
+ /// Retrieve the C fexcept_t type.
+ QualType getfexcept_tType() const {
+ if (fexcept_tDecl)
+ return getTypeDeclType(ElaboratedTypeKeyword::None,
+ /*Qualifier=*/std::nullopt, fexcept_tDecl);
+ return QualType();
+ }
+
+ /// Set the type for the C fenv_t type.
+ void setfenv_tDecl(TypeDecl *fenv_tDecl) { this->fenv_tDecl = fenv_tDecl; }
+
+ /// Retrieve the C fenv_t type.
+ QualType getfenv_tType() const {
+ if (fenv_tDecl)
+ return getTypeDeclType(ElaboratedTypeKeyword::None,
+ /*Qualifier=*/std::nullopt, fenv_tDecl);
+ return QualType();
+ }
+
/// The result type of logical operations, '<', '>', '!=', etc.
CanQualType getLogicalOperationType() const {
return getLangOpts().CPlusPlus ? BoolTy : IntTy;
@@ -2614,7 +2644,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
GE_Missing_setjmp,
/// Missing a type from <ucontext.h>
- GE_Missing_ucontext
+ GE_Missing_ucontext,
+
+ /// Missing a type from <fenv.h>
+ GE_Missing_fenv
};
QualType DecodeTypeStr(const char *&Str, const ASTContext &Context,
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 8b9f613037718..9a2e5c1c198e1 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -873,6 +873,8 @@ NOTABLE_IDENTIFIER(FILE)
NOTABLE_IDENTIFIER(jmp_buf)
NOTABLE_IDENTIFIER(sigjmp_buf)
NOTABLE_IDENTIFIER(ucontext_t)
+NOTABLE_IDENTIFIER(fexcept_t)
+NOTABLE_IDENTIFIER(fenv_t)
NOTABLE_IDENTIFIER(float_t)
NOTABLE_IDENTIFIER(double_t)
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 4e8fe1d32d42e..c6e5357ae147b 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1214,7 +1214,13 @@ enum SpecialTypeIDs {
SPECIAL_TYPE_OBJC_SEL_REDEFINITION = 6,
/// C ucontext_t typedef type
- SPECIAL_TYPE_UCONTEXT_T = 7
+ SPECIAL_TYPE_UCONTEXT_T = 7,
+
+ /// C fexcept_t typedef type
+ SPECIAL_TYPE_FEXCEPT_T = 8,
+
+ /// C fenv_t typedef type
+ SPECIAL_TYPE_FENV_T = 9
};
/// The number of special type IDs.
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a8cfdca1cb96d..343e8034d27aa 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -12822,6 +12822,25 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
case 'm':
Type = Context.MFloat8Ty;
break;
+ case 'T':
+ switch (*Str++) {
+ case 'x': {
+ Type = Context.getfexcept_tType();
+ break;
+ }
+ case 'e': {
+ Type = Context.getfenv_tType();
+ break;
+ }
+ default: {
+ llvm_unreachable("Unexpected target builtin type");
+ }
+ }
+ if (Type.isNull()) {
+ Error = ASTContext::GE_Missing_fenv;
+ return {};
+ }
+ break;
}
// If there are modifiers and if we're allowed to parse them, go for it.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 2951fd09294d8..45de3d89e3bb1 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2377,6 +2377,8 @@ static StringRef getHeaderName(Builtin::Context &BuiltinInfo, unsigned ID,
return "setjmp.h";
case ASTContext::GE_Missing_ucontext:
return "ucontext.h";
+ case ASTContext::GE_Missing_fenv:
+ return "fenv.h";
}
llvm_unreachable("unhandled error kind");
}
@@ -6994,6 +6996,12 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD,
case tok::NotableIdentifierKind::ucontext_t:
Context.setucontext_tDecl(NewTD);
break;
+ case tok::NotableIdentifierKind::fexcept_t:
+ Context.setfexcept_tDecl(NewTD);
+ break;
+ case tok::NotableIdentifierKind::fenv_t:
+ Context.setfenv_tDecl(NewTD);
+ break;
case tok::NotableIdentifierKind::float_t:
case tok::NotableIdentifierKind::double_t:
NewTD->addAttr(AvailableOnlyInDefaultEvalMethodAttr::Create(Context));
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 7c3a6fceb3623..0293273fd35cc 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -5689,6 +5689,42 @@ void ASTReader::InitializeContext() {
}
}
}
+
+ if (TypeID Fexcept_t = SpecialTypes[SPECIAL_TYPE_FEXCEPT_T]) {
+ QualType Fexcept_tType = GetType(Fexcept_t);
+ if (Fexcept_tType.isNull()) {
+ Error("fexcept_t type is NULL");
+ return;
+ }
+
+ if (!Context.fexcept_tDecl) {
+ if (const TypedefType *Typedef = Fexcept_tType->getAs<TypedefType>())
+ Context.setfexcept_tDecl(Typedef->getDecl());
+ else {
+ const TagType *Tag = Fexcept_tType->getAs<TagType>();
+ assert(Tag && "Invalid fexcept_t type in AST file");
+ Context.setfexcept_tDecl(Tag->getDecl());
+ }
+ }
+ }
+
+ if (TypeID Fenv_t = SpecialTypes[SPECIAL_TYPE_FENV_T]) {
+ QualType Fenv_tType = GetType(Fenv_t);
+ if (Fenv_tType.isNull()) {
+ Error("fenv_t type is NULL");
+ return;
+ }
+
+ if (!Context.fenv_tDecl) {
+ if (const TypedefType *Typedef = Fenv_tType->getAs<TypedefType>())
+ Context.setfenv_tDecl(Typedef->getDecl());
+ else {
+ const TagType *Tag = Fenv_tType->getAs<TagType>();
+ assert(Tag && "Invalid fenv_t type in AST file");
+ Context.setfenv_tDecl(Tag->getDecl());
+ }
+ }
+ }
}
ReadPragmaDiagnosticMappings(Context.getDiagnostics());
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 9f05daaf65850..b7255b724f76c 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -6109,6 +6109,8 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema *SemaPtr, StringRef isysroot,
AddTypeRef(Context, Context.ObjCClassRedefinitionType, SpecialTypes);
AddTypeRef(Context, Context.ObjCSelRedefinitionType, SpecialTypes);
AddTypeRef(Context, Context.getucontext_tType(), SpecialTypes);
+ AddTypeRef(Context, Context.getfexcept_tType(), SpecialTypes);
+ AddTypeRef(Context, Context.getfenv_tType(), SpecialTypes);
}
if (SemaPtr)
diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
index f628a993a23cc..d31ff1bb1482e 100644
--- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
+++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
@@ -374,6 +374,8 @@ class PrototypeParser {
.Case("uint64_t", "UWi")
.Case("void", "v")
.Case("wchar_t", "w")
+ .Case("fexcept_t", "Tx")
+ .Case("fenv_t", "Te")
.Case("...", ".")
.Default("error");
if (ReturnTypeVal == "error")
>From 132bb6438035707945281957c4a872efb25dca1a Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Sun, 29 Mar 2026 15:28:56 +0200
Subject: [PATCH 04/10] Added warning for fegetexceptflag, fesetexceptflag,
fegetenv, feholdexcept, fesetenv, feupdateenv being used without appropriate
flags/pragmas (#128239)
---
clang/include/clang/Basic/Builtins.td | 30 +++++++++++++++++++++++++++
clang/lib/Sema/SemaChecking.cpp | 6 ++++++
2 files changed, 36 insertions(+)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 510241f03102b..16f4d0bde22cb 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4414,11 +4414,21 @@ def FeClearExcept : LibBuiltin<"fenv.h"> {
let Prototype = "int(int)";
}
+def FeGetExceptFlag : LibBuiltin<"fenv.h"> {
+ let Spellings = ["fegetexceptflag"];
+ let Prototype = "int(fexcept_t*, int)";
+}
+
def FeRaiseExcept : LibBuiltin<"fenv.h"> {
let Spellings = ["feraiseexcept"];
let Prototype = "int(int)";
}
+def FeSetExceptFlag : LibBuiltin<"fenv.h"> {
+ let Spellings = ["fesetexceptflag"];
+ let Prototype = "int(fexcept_t const*, int)";
+}
+
def FeTestExcept : LibBuiltin<"fenv.h"> {
let Spellings = ["fetestexcept"];
let Prototype = "int(int)";
@@ -4434,6 +4444,26 @@ def FeSetRound : LibBuiltin<"fenv.h"> {
let Prototype = "int(int)";
}
+def FeGetEnv : LibBuiltin<"fenv.h"> {
+ let Spellings = ["fegetenv"];
+ let Prototype = "int(fenv_t*)";
+}
+
+def FeHoldExcept : LibBuiltin<"fenv.h"> {
+ let Spellings = ["feholdexcept"];
+ let Prototype = "int(fenv_t*)";
+}
+
+def FeSetEnv : LibBuiltin<"fenv.h"> {
+ let Spellings = ["fesetenv"];
+ let Prototype = "int(fenv_t const*)";
+}
+
+def FeUpdateEnv : LibBuiltin<"fenv.h"> {
+ let Spellings = ["feupdateenv"];
+ let Prototype = "int(fenv_t const*)";
+}
+
def __Addressof : LangBuiltin<"CXX_LANG"> {
let Spellings = ["__addressof"];
let Attributes = [FunctionWithoutBuiltinPrefix, NoThrow, Const,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 7f80880d83273..3796e42d542b1 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3845,10 +3845,16 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
break;
case Builtin::BIfeclearexcept:
+ case Builtin::BIfegetexceptflag:
case Builtin::BIferaiseexcept:
+ case Builtin::BIfesetexceptflag:
case Builtin::BIfetestexcept:
case Builtin::BIfegetround:
case Builtin::BIfesetround:
+ case Builtin::BIfegetenv:
+ case Builtin::BIfeholdexcept:
+ case Builtin::BIfesetenv:
+ case Builtin::BIfeupdateenv:
if (TheCall->getFPFeaturesInEffect(getLangOpts()).getExceptionMode() ==
LangOptions::FPE_Ignore) {
Diag(TheCall->getBeginLoc(), diag::warn_fe_access_without_fenv_access)
>From 6a781c5556379087515b8a3722813811e49afd6f Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Sun, 29 Mar 2026 15:29:25 +0200
Subject: [PATCH 05/10] Added additional regression tests for fenv access
warning
---
clang/test/Sema/fenv-access.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/clang/test/Sema/fenv-access.c b/clang/test/Sema/fenv-access.c
index 57d4debfa9c73..3a7b95af7ab4c 100644
--- a/clang/test/Sema/fenv-access.c
+++ b/clang/test/Sema/fenv-access.c
@@ -2,36 +2,67 @@
// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=maytrap -DNO_WARN %s
// RUN: %clang_cc1 -verify -Wfenv-access -ffp-exception-behavior=strict -DNO_WARN %s
+typedef struct {} fenv_t;
+typedef unsigned short int fexcept_t;
+
int feclearexcept(int excepts);
+int fegetexceptflag(fexcept_t *flagp, int excepts);
int feraiseexcept(int excepts);
+int fesetexceptflag(const fexcept_t *flagp, int excepts);
int fetestexcept(int excepts);
int fegetround(void);
int fesetround(int rounding_mode);
+int fegetenv(fenv_t *envp);
+int feholdexcept(fenv_t *envp);
+int fesetenv(const fenv_t *envp);
+int feupdateenv(const fenv_t *envp);
#define FE_INVALID 1
+fexcept_t *flagp = 0;
+fenv_t *envp = 0;
+
void test_fenv_access_off(void) {
#ifdef NO_WARN
// expected-no-diagnostics
feclearexcept(FE_INVALID);
+ fegetexceptflag(flagp, FE_INVALID);
feraiseexcept(FE_INVALID);
+ fesetexceptflag(flagp, FE_INVALID);
fetestexcept(FE_INVALID);
fegetround();
fesetround(0);
+ fegetenv(envp);
+ feholdexcept(envp);
+ fesetenv(envp);
+ feupdateenv(envp);
#else
feclearexcept(FE_INVALID); // expected-warning {{'feclearexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ fegetexceptflag(flagp, FE_INVALID); // expected-warning {{'fegetexceptflag' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
feraiseexcept(FE_INVALID); // expected-warning {{'feraiseexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ fesetexceptflag(flagp, FE_INVALID); // expected-warning {{'fesetexceptflag' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
fetestexcept(FE_INVALID); // expected-warning {{'fetestexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
fegetround(); // expected-warning {{'fegetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
fesetround(0); // expected-warning {{'fesetround' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ fegetenv(envp); // expected-warning {{'fegetenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ feholdexcept(envp); // expected-warning {{'feholdexcept' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ fesetenv(envp); // expected-warning {{'fesetenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
+ feupdateenv(envp); // expected-warning {{'feupdateenv' used without enabling floating-point exception behavior; use 'pragma STDC FENV_ACCESS ON' or compile with '-ffp-exception-behavior=maytrap'}}
#endif
}
void test_fenv_access_on(void) {
#pragma STDC FENV_ACCESS ON
+ fesetround(0);
feclearexcept(FE_INVALID);
+ fegetexceptflag(flagp, FE_INVALID);
feraiseexcept(FE_INVALID);
+ fesetexceptflag(flagp, FE_INVALID);
fetestexcept(FE_INVALID);
fegetround();
fesetround(0);
+ fegetenv(envp);
+ feholdexcept(envp);
+ fesetenv(envp);
+ feupdateenv(envp);
}
>From 3cd4bf04824ce877111d5c06f5205fa73ac3ccbc Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Fri, 3 Apr 2026 11:34:10 +0200
Subject: [PATCH 06/10] Added isPotentiallyEvaluated() check to avoid
diagnostics inside of decltype() or similar constructs
---
clang/include/clang/Sema/Sema.h | 4 ++++
clang/lib/Sema/SemaChecking.cpp | 3 ++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4e6058b8e5f79..b81b1f4caf987 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8249,6 +8249,10 @@ class Sema final : public SemaBase {
return currentEvaluationContext().isUnevaluated();
}
+ bool isPotentiallyEvaluatedContext() const {
+ return currentEvaluationContext().isPotentiallyEvaluated();
+ }
+
bool isImmediateFunctionContext() const {
return currentEvaluationContext().isImmediateFunctionContext();
}
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 3796e42d542b1..7c6664954e850 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3856,7 +3856,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
case Builtin::BIfesetenv:
case Builtin::BIfeupdateenv:
if (TheCall->getFPFeaturesInEffect(getLangOpts()).getExceptionMode() ==
- LangOptions::FPE_Ignore) {
+ LangOptions::FPE_Ignore &&
+ isPotentiallyEvaluatedContext()) {
Diag(TheCall->getBeginLoc(), diag::warn_fe_access_without_fenv_access)
<< FDecl->getName() << TheCall->getSourceRange();
}
>From 7e23d5123712949bf1f78c9df644863a99ebee7e Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Wed, 8 Apr 2026 20:40:49 +0200
Subject: [PATCH 07/10] Added regression tests for unevaluated occurrences of
fp exception functions
---
clang/test/Sema/fenv-access-unevaluated.cpp | 31 +++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 clang/test/Sema/fenv-access-unevaluated.cpp
diff --git a/clang/test/Sema/fenv-access-unevaluated.cpp b/clang/test/Sema/fenv-access-unevaluated.cpp
new file mode 100644
index 0000000000000..14752ba3c377e
--- /dev/null
+++ b/clang/test/Sema/fenv-access-unevaluated.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -verify -Wfenv-access %s
+
+typedef struct {} fenv_t;
+typedef unsigned short int fexcept_t;
+
+int feclearexcept(int excepts);
+int fegetexceptflag(fexcept_t *flagp, int excepts);
+int feraiseexcept(int excepts);
+int fesetexceptflag(const fexcept_t *flagp, int excepts);
+int fetestexcept(int excepts);
+int fegetround(void);
+int fesetround(int rounding_mode);
+int fegetenv(fenv_t *envp);
+int feholdexcept(fenv_t *envp);
+int fesetenv(const fenv_t *envp);
+int feupdateenv(const fenv_t *envp);
+
+// expected-no-diagnostics
+void test_fenv_access_unevaluated() {
+ decltype(::feclearexcept) a;
+ decltype(::fegetexceptflag) b;
+ decltype(::feraiseexcept) c;
+ decltype(::fesetexceptflag) d;
+ decltype(::fetestexcept) e;
+ decltype(::fegetround) f;
+ decltype(::fesetround) g;
+ decltype(::fegetenv) h;
+ decltype(::feholdexcept) i;
+ decltype(::fesetenv) j;
+ decltype(::feupdateenv) k;
+}
>From 8efc8ce561e8b8595fb2dce5650b12ae5f6c8efc Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Thu, 9 Apr 2026 21:01:53 +0200
Subject: [PATCH 08/10] Added regression tests for implicit usage of fp
exception functions
---
clang/test/Sema/fenv-access-implicit.c | 35 ++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 clang/test/Sema/fenv-access-implicit.c
diff --git a/clang/test/Sema/fenv-access-implicit.c b/clang/test/Sema/fenv-access-implicit.c
new file mode 100644
index 0000000000000..0c4bd6b0eb855
--- /dev/null
+++ b/clang/test/Sema/fenv-access-implicit.c
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -verify -Wfenv-access %s
+
+typedef struct {} fenv_t;
+typedef unsigned short int fexcept_t;
+
+fexcept_t *flagp = 0;
+fenv_t *envp = 0;
+
+#define FE_INVALID 1
+
+void test_fenv_access_undeclared(void) {
+ #pragma STDC FENV_ACCESS ON
+ feclearexcept(FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feclearexcept'}} \
+ expected-error {{call to undeclared library function 'feclearexcept' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}}
+ fegetexceptflag(flagp, FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fegetexceptflag'}} \
+ expected-error {{call to undeclared library function 'fegetexceptflag' with type 'int (fexcept_t *, int)' (aka 'int (unsigned short *, int)'); ISO C99 and later do not support implicit function declarations}}
+ feraiseexcept(FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feraiseexcept'}} \
+ expected-error {{call to undeclared library function 'feraiseexcept' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}}
+ fesetexceptflag(flagp, FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fesetexceptflag'}} \
+ expected-error {{call to undeclared library function 'fesetexceptflag' with type 'int (const fexcept_t *, int)' (aka 'int (const unsigned short *, int)'); ISO C99 and later do not support implicit function declarations}}
+ fetestexcept(FE_INVALID); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fetestexcept'}} \
+ expected-error {{call to undeclared library function 'fetestexcept' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}}
+ fegetround(); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fegetround'}} \
+ expected-error {{call to undeclared library function 'fegetround' with type 'int (void)'; ISO C99 and later do not support implicit function declarations}}
+ fesetround(0); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fesetround'}} \
+ expected-error {{call to undeclared library function 'fesetround' with type 'int (int)'; ISO C99 and later do not support implicit function declarations}}
+ fegetenv(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fegetenv'}} \
+ expected-error {{call to undeclared library function 'fegetenv' with type 'int (fenv_t *)'; ISO C99 and later do not support implicit function declarations}}
+ feholdexcept(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feholdexcept'}} \
+ expected-error {{call to undeclared library function 'feholdexcept' with type 'int (fenv_t *)'; ISO C99 and later do not support implicit function declarations}}
+ fesetenv(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'fesetenv'}} \
+ expected-error {{call to undeclared library function 'fesetenv' with type 'int (const fenv_t *)'; ISO C99 and later do not support implicit function declarations}}
+ feupdateenv(envp); // expected-note {{include the header <fenv.h> or explicitly provide a declaration for 'feupdateenv'}} \
+ expected-error {{call to undeclared library function 'feupdateenv' with type 'int (const fenv_t *)'; ISO C99 and later do not support implicit function declarations}}
+}
>From ff6671ec9587449434b827334151a3cee24f4c10 Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Sun, 12 Apr 2026 17:12:09 +0200
Subject: [PATCH 09/10] Added regression tests for a function declaration that
does not match and AST serialization (fenv.h)
---
clang/test/Sema/builtin-fenv.c | 55 ++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 clang/test/Sema/builtin-fenv.c
diff --git a/clang/test/Sema/builtin-fenv.c b/clang/test/Sema/builtin-fenv.c
new file mode 100644
index 0000000000000..739528b953c00
--- /dev/null
+++ b/clang/test/Sema/builtin-fenv.c
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DWRONG_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1,CHECK2
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DRIGHT_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1,CHECK2
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DONLY_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1,CHECK2
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=c,expected -DNO_FEGETEXCEPTFLAG %s -ast-dump 2>&1 | FileCheck %s --check-prefixes=CHECK1
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=cxx,expected -x c++ -DWRONG_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=cxx,expected -x c++ -DRIGHT_FEXCEPT_T %s -ast-dump | FileCheck %s --check-prefixes=CHECK1,CHECK2
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=cxx,expected -x c++ -DONLY_FEXCEPT_T %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -verify=cxx,expected -x c++ -DNO_FEGETEXCEPTFLAG %s
+
+// tests inspired by clang/test/Sema/builtin-setjmp.c
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if WRONG_FEXCEPT_T
+typedef unsigned short int fexcept_t;
+extern int fegetexceptflag(int, int); // c-warning {{incompatible redeclaration of library function 'fegetexceptflag'}}
+ // c-note at -1 {{'fegetexceptflag' is a builtin with type 'int (fexcept_t *, int)' (aka 'int (unsigned short *, int)')}}
+#elif RIGHT_FEXCEPT_T
+// c-no-diagnostics
+typedef unsigned short int fexcept_t;
+extern int fegetexceptflag(unsigned short int *, int); // OK, right type.
+#elif ONLY_FEXCEPT_T
+typedef long *fexcept_t;
+#endif
+
+void use(void) {
+ #pragma STDC FENV_ACCESS ON
+ fegetexceptflag(0, 0);
+ #if NO_FEGETEXCEPTFLAG
+ // cxx-error at -2 {{undeclared identifier 'fegetexceptflag'}}
+ // c-error at -3 {{call to undeclared function 'fegetexceptflag'; ISO C99 and later do not support implicit function declarations}}
+ // c-warning at -4 {{declaration of built-in function 'fegetexceptflag' requires inclusion of the header <fenv.h>}}
+ #elif ONLY_FEXCEPT_T
+ // cxx-error at -6 {{undeclared identifier 'fegetexceptflag'}}
+ // c-error at -7 {{call to undeclared library function 'fegetexceptflag' with type 'int (fexcept_t *, int)' (aka 'int (long **, int)'); ISO C99 and later do not support implicit function declarations}}
+ // c-note at -8 {{include the header <fenv.h> or explicitly provide a declaration for 'fegetexceptflag'}}
+ #else
+ // cxx-no-diagnostics
+ #endif
+
+ #ifdef NO_FEGETEXCEPTFLAG
+ // In this case, the regular AST dump doesn't dump the implicit declaration of 'fegetexceptflag'.
+ #pragma clang __debug dump fegetexceptflag
+ #endif
+}
+
+// CHECK1: FunctionDecl {{.*}} used fegetexceptflag
+// CHECK2: BuiltinAttr {{.*}} Implicit
+
+
+#ifdef __cplusplus
+}
+#endif
>From 2350e9de3a6423ab14f4d25f6b7f2f54ebfd58b9 Mon Sep 17 00:00:00 2001
From: Marcos Ramirez Joos <87144291+maarcosrmz at users.noreply.github.com>
Date: Tue, 14 Apr 2026 08:25:59 +0200
Subject: [PATCH 10/10] Added regression tests for AST serialization (fenv.h)
---
clang/test/PCH/builtins-fenv.c | 27 +++++++++++++++++++++++++++
clang/test/PCH/builtins-fenv.h | 18 ++++++++++++++++++
2 files changed, 45 insertions(+)
create mode 100644 clang/test/PCH/builtins-fenv.c
create mode 100644 clang/test/PCH/builtins-fenv.h
diff --git a/clang/test/PCH/builtins-fenv.c b/clang/test/PCH/builtins-fenv.c
new file mode 100644
index 0000000000000..61d16302d1ce4
--- /dev/null
+++ b/clang/test/PCH/builtins-fenv.c
@@ -0,0 +1,27 @@
+// Test this without pch.
+// RUN: %clang_cc1 -include %S/builtins-fenv.h -fsyntax-only -verify %s
+
+// Test with pch.
+// RUN: %clang_cc1 -emit-pch -o %t %S/builtins-fenv.h
+// RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s
+
+// expected-no-diagnostics
+#define FE_INVALID 1
+
+fexcept_t *flagp = 0;
+fenv_t *envp = 0;
+
+void f(void) {
+ #pragma STDC FENV_ACCESS ON
+ feclearexcept(FE_INVALID);
+ fegetexceptflag(flagp, FE_INVALID);
+ feraiseexcept(FE_INVALID);
+ fesetexceptflag(flagp, FE_INVALID);
+ fetestexcept(FE_INVALID);
+ fegetround();
+ fesetround(0);
+ fegetenv(envp);
+ feholdexcept(envp);
+ fesetenv(envp);
+ feupdateenv(envp);
+}
diff --git a/clang/test/PCH/builtins-fenv.h b/clang/test/PCH/builtins-fenv.h
new file mode 100644
index 0000000000000..c53e66a3d941c
--- /dev/null
+++ b/clang/test/PCH/builtins-fenv.h
@@ -0,0 +1,18 @@
+// Header for PCH test builtins-fenv.c
+typedef struct {} fenv_t;
+typedef unsigned short int fexcept_t;
+
+int feclearexcept(int excepts);
+int fegetexceptflag(fexcept_t *flagp, int excepts);
+int feraiseexcept(int excepts);
+int fesetexceptflag(const fexcept_t *flagp, int excepts);
+int fetestexcept(int excepts);
+int fegetround(void);
+int fesetround(int rounding_mode);
+int fegetenv(fenv_t *envp);
+int feholdexcept(fenv_t *envp);
+int fesetenv(const fenv_t *envp);
+int feupdateenv(const fenv_t *envp);
+
+#define FE_INVALID 1
+
More information about the cfe-commits
mailing list