[flang-commits] [flang] [llvm] [Flang] Introduce common::numeric_limits (PR #228108)
Michael Kruse via flang-commits
flang-commits at lists.llvm.org
Tue Oct 6 15:46:01 PDT 2026
https://github.com/Meinersbur updated https://github.com/llvm/llvm-project/pull/228108
>From 53a59e478e4e82eec5aff821971c4e7a52548bf5 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Thu, 1 Oct 2026 13:20:05 +0200
Subject: [PATCH 01/15] Introduce common::numeric_limits
---
.../flang-rt/runtime/numeric-templates.h | 23 ++--
flang-rt/lib/quadmath/math-entries.h | 4 +-
flang-rt/lib/runtime/command.cpp | 2 +-
flang-rt/lib/runtime/complex-powi.cpp | 4 +-
flang-rt/lib/runtime/namelist.cpp | 2 +-
flang-rt/lib/runtime/numeric.cpp | 8 +-
flang-rt/lib/runtime/time-intrinsic.cpp | 4 +-
flang-rt/lib/runtime/tools.cpp | 2 +-
flang-rt/lib/runtime/transformational.cpp | 2 +-
flang-rt/unittests/Runtime/ExternalIOTest.cpp | 11 +-
flang-rt/unittests/Runtime/Numeric.cpp | 96 ++++++++-------
.../unittests/Runtime/Transformational.cpp | 7 +-
flang/include/flang/Common/erfc-scaled.h | 6 +-
flang/include/flang/Common/uint128.h | 111 +++++++++++++++---
flang/lib/Decimal/big-radix-floating-point.h | 4 +-
flang/lib/Evaluate/constant.cpp | 2 +-
16 files changed, 180 insertions(+), 108 deletions(-)
diff --git a/flang-rt/include/flang-rt/runtime/numeric-templates.h b/flang-rt/include/flang-rt/runtime/numeric-templates.h
index e41c7f079e9215..7733a32c98f590 100644
--- a/flang-rt/include/flang-rt/runtime/numeric-templates.h
+++ b/flang-rt/include/flang-rt/runtime/numeric-templates.h
@@ -35,17 +35,8 @@ template <TypeCategory CAT, int KIND, bool IS_MAXVAL, typename Enable = void>
struct MaxOrMinIdentity {
using Type = CppTypeFor<CAT, KIND>;
static constexpr RT_API_ATTRS Type Value() {
- return IS_MAXVAL ? std::numeric_limits<Type>::lowest()
- : std::numeric_limits<Type>::max();
- }
-};
-
-// std::numeric_limits<> may not know int128_t
-template <bool IS_MAXVAL>
-struct MaxOrMinIdentity<TypeCategory::Integer, 16, IS_MAXVAL> {
- using Type = CppTypeFor<TypeCategory::Integer, 16>;
- static constexpr RT_API_ATTRS Type Value() {
- return IS_MAXVAL ? Type{1} << 127 : ~Type{0} >> 1;
+ return IS_MAXVAL ? common::numeric_limits<Type>::lowest()
+ : common::numeric_limits<Type>::max();
}
};
@@ -90,7 +81,7 @@ struct MaxOrMinIdentity<TypeCategory::Real, 16, IS_MAXVAL,
// Minimum finite representable value.
// For floating-point types, returns minimum positive normalized value.
template <int PREC, typename T> struct MinValue {
- static RT_API_ATTRS T get() { return std::numeric_limits<T>::min(); }
+ static RT_API_ATTRS T get() { return common::numeric_limits<T>::min(); }
};
template <typename T> struct MinValue<11, T> {
// TINY(0._2)
@@ -160,7 +151,7 @@ template <typename T> struct LDEXPTy {
template <typename T> struct MAXTy {
static constexpr RT_API_ATTRS T compute() {
- return std::numeric_limits<T>::max();
+ return common::numeric_limits<T>::max();
}
};
@@ -178,7 +169,7 @@ template <int PREC, typename T> struct MINTy {
template <typename T> struct QNANTy {
static constexpr RT_API_ATTRS T compute() {
- return std::numeric_limits<T>::quiet_NaN();
+ return common::numeric_limits<T>::quiet_NaN();
}
};
@@ -224,8 +215,8 @@ template <typename T> inline RT_API_ATTRS T SetExponent(T x, std::int64_t p) {
int expo{ILOGBTy<T>::compute(x) + 1};
auto ip{static_cast<int>(p - expo)};
if (ip != p - expo) {
- ip = p < 0 ? std::numeric_limits<int>::min()
- : std::numeric_limits<int>::max();
+ ip = p < 0 ? common::numeric_limits<int>::min()
+ : common::numeric_limits<int>::max();
}
return LDEXPTy<T>::compute(x, ip); // x*2**(p-e)
}
diff --git a/flang-rt/lib/quadmath/math-entries.h b/flang-rt/lib/quadmath/math-entries.h
index 91ad80c8197f49..75cfa7f2041fe6 100644
--- a/flang-rt/lib/quadmath/math-entries.h
+++ b/flang-rt/lib/quadmath/math-entries.h
@@ -220,9 +220,9 @@ DEFINE_SIMPLE_ALIAS(Yn, ynl)
// Use numeric_limits to produce infinity of the right type.
#define F128_RT_INFINITY \
- (std::numeric_limits<CppTypeFor<TypeCategory::Real, 16>>::infinity())
+ (common::numeric_limits<CppTypeFor<TypeCategory::Real, 16>>::infinity())
#define F128_RT_QNAN \
- (std::numeric_limits<CppTypeFor<TypeCategory::Real, 16>>::quiet_NaN())
+ (common::numeric_limits<CppTypeFor<TypeCategory::Real, 16>>::quiet_NaN())
#elif HAS_LIBMF128
// We can use __float128 versions of libm functions.
// __STDC_WANT_IEC_60559_TYPES_EXT__ needs to be defined
diff --git a/flang-rt/lib/runtime/command.cpp b/flang-rt/lib/runtime/command.cpp
index f0f4e8bf64e997..66e1fc787dfa0f 100644
--- a/flang-rt/lib/runtime/command.cpp
+++ b/flang-rt/lib/runtime/command.cpp
@@ -87,7 +87,7 @@ template <int KIND> struct FitsInIntegerKind {
if constexpr (KIND >= 8) {
return true;
} else {
- return value <= std::numeric_limits<Fortran::runtime::CppTypeFor<
+ return value <= common::numeric_limits<Fortran::runtime::CppTypeFor<
Fortran::common::TypeCategory::Integer, KIND>>::max();
}
}
diff --git a/flang-rt/lib/runtime/complex-powi.cpp b/flang-rt/lib/runtime/complex-powi.cpp
index a561d114591cf2..61b6f4b265978f 100644
--- a/flang-rt/lib/runtime/complex-powi.cpp
+++ b/flang-rt/lib/runtime/complex-powi.cpp
@@ -24,10 +24,10 @@ template <typename C, typename I> C tgpowi(C base, I exp) {
}
bool invertResult{exp < 0};
- bool isMin{exp == std::numeric_limits<I>::min()};
+ bool isMin{exp == common::numeric_limits<I>::min()};
if (isMin) {
- exp = std::numeric_limits<I>::max();
+ exp = common::numeric_limits<I>::max();
}
if (exp < 0) {
diff --git a/flang-rt/lib/runtime/namelist.cpp b/flang-rt/lib/runtime/namelist.cpp
index 231b1705a4a2fd..2f248208b88b59 100644
--- a/flang-rt/lib/runtime/namelist.cpp
+++ b/flang-rt/lib/runtime/namelist.cpp
@@ -138,7 +138,7 @@ static RT_API_ATTRS common::optional<SubscriptValue> GetSubscriptValue(
bool overflow{false};
while (ch && *ch >= '0' && *ch <= '9') {
SubscriptValue was{value.value_or(0)};
- overflow |= was >= std::numeric_limits<SubscriptValue>::max() / 10;
+ overflow |= was >= common::numeric_limits<SubscriptValue>::max() / 10;
value = 10 * was + *ch - '0';
io.HandleRelativePosition(byteCount);
ch = io.GetCurrentChar(byteCount);
diff --git a/flang-rt/lib/runtime/numeric.cpp b/flang-rt/lib/runtime/numeric.cpp
index 78f148dbc5d8ce..835897680d6f1f 100644
--- a/flang-rt/lib/runtime/numeric.cpp
+++ b/flang-rt/lib/runtime/numeric.cpp
@@ -190,9 +190,9 @@ inline RT_API_ATTRS CppTypeFor<TypeCategory::Integer, 4> SelectedRealKind(
template <int PREC, typename T>
inline RT_API_ATTRS T Nearest(T x, bool positive) {
if (positive) {
- return std::nextafter(x, std::numeric_limits<T>::infinity());
+ return std::nextafter(x, common::numeric_limits<T>::infinity());
} else {
- return std::nextafter(x, -std::numeric_limits<T>::infinity());
+ return std::nextafter(x, -common::numeric_limits<T>::infinity());
}
}
@@ -202,9 +202,9 @@ RT_API_ATTRS BTy FPowI(BTy base, ETy exp) {
if (exp == ETy{0})
return BTy{1};
bool isNegativePower{exp < ETy{0}};
- bool isMinPower{exp == std::numeric_limits<ETy>::min()};
+ bool isMinPower{exp == common::numeric_limits<ETy>::min()};
if (isMinPower) {
- exp = std::numeric_limits<ETy>::max();
+ exp = common::numeric_limits<ETy>::max();
} else if (isNegativePower) {
exp = -exp;
}
diff --git a/flang-rt/lib/runtime/time-intrinsic.cpp b/flang-rt/lib/runtime/time-intrinsic.cpp
index 89e199a1bc79d7..d78f5990d5ce70 100644
--- a/flang-rt/lib/runtime/time-intrinsic.cpp
+++ b/flang-rt/lib/runtime/time-intrinsic.cpp
@@ -270,7 +270,7 @@ template <int KIND> struct StoreNegativeHugeAt {
const Fortran::runtime::Descriptor &result, std::size_t at) const {
*result.ZeroBasedIndexedElement<Fortran::runtime::CppTypeFor<
Fortran::common::TypeCategory::Integer, KIND>>(at) =
- -std::numeric_limits<Fortran::runtime::CppTypeFor<
+ -common::numeric_limits<Fortran::runtime::CppTypeFor<
Fortran::common::TypeCategory::Integer, KIND>>::max();
}
};
@@ -383,7 +383,7 @@ GetGmtOffset(const TM &tm, fallback_implementation) {
// tm.tm_gmtoff is not available, there may be platform dependent alternatives
// (such as using timezone from <time.h> when available), but so far just
// return -HUGE to report that this information is not available.
- const auto negHuge{-std::numeric_limits<Fortran::runtime::CppTypeFor<
+ const auto negHuge{-common::numeric_limits<Fortran::runtime::CppTypeFor<
Fortran::common::TypeCategory::Integer, KIND>>::max()};
#if defined _AIX
bool err{false};
diff --git a/flang-rt/lib/runtime/tools.cpp b/flang-rt/lib/runtime/tools.cpp
index 8ef7106f6a7c57..45a56fd7d71e70 100644
--- a/flang-rt/lib/runtime/tools.cpp
+++ b/flang-rt/lib/runtime/tools.cpp
@@ -330,7 +330,7 @@ template <int KIND> struct FitsInIntegerKind {
return true;
} else {
return value <=
- std::numeric_limits<
+ common::numeric_limits<
CppTypeFor<Fortran::common::TypeCategory::Integer, KIND>>::max();
}
}
diff --git a/flang-rt/lib/runtime/transformational.cpp b/flang-rt/lib/runtime/transformational.cpp
index 395721ca2b9bd9..4e6f24ed79e8c4 100644
--- a/flang-rt/lib/runtime/transformational.cpp
+++ b/flang-rt/lib/runtime/transformational.cpp
@@ -320,7 +320,7 @@ static inline RT_API_ATTRS void DoBesselYnX0(Descriptor &result, int32_t n1,
for (int j{1}; j <= n2 - n1 + 1; ++j) {
at[0] = j;
*result.Element<CppTypeFor<CAT, KIND>>(at) =
- -std::numeric_limits<CppTypeFor<CAT, KIND>>::infinity();
+ -common::numeric_limits<CppTypeFor<CAT, KIND>>::infinity();
}
}
diff --git a/flang-rt/unittests/Runtime/ExternalIOTest.cpp b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
index 1fe3b08126c5d0..cf469e5b34d400 100644
--- a/flang-rt/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang-rt/unittests/Runtime/ExternalIOTest.cpp
@@ -978,12 +978,15 @@ TEST(ExternalIOTests, TestUCS) {
}
TEST(ExternalIOTests, BigUnitNumbers) {
- if (std::numeric_limits<ExternalUnit>::max() <
- std::numeric_limits<std::int64_t>::max()) {
- std::int64_t unit64Ok = std::numeric_limits<ExternalUnit>::max();
+ if (Fortran::common::numeric_limits<ExternalUnit>::max() <
+ Fortran::common::numeric_limits<std::int64_t>::max()) {
+ std::int64_t unit64Ok =
+ Fortran::common::numeric_limits<ExternalUnit>::max();
std::int64_t unit64Bad = unit64Ok + 1;
std::int64_t unit64Bad2 =
- static_cast<std::int64_t>(std::numeric_limits<ExternalUnit>::min()) - 1;
+ static_cast<std::int64_t>(
+ Fortran::common::numeric_limits<ExternalUnit>::min()) -
+ 1;
EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, true), IostatOk);
EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, false), IostatOk);
EXPECT_EQ(
diff --git a/flang-rt/unittests/Runtime/Numeric.cpp b/flang-rt/unittests/Runtime/Numeric.cpp
index 4baad3fc9ad0eb..1c8ccc42626680 100644
--- a/flang-rt/unittests/Runtime/Numeric.cpp
+++ b/flang-rt/unittests/Runtime/Numeric.cpp
@@ -13,6 +13,7 @@
#include <limits>
using namespace Fortran::runtime;
+using Fortran::common::numeric_limits;
using Fortran::common::TypeCategory;
template <int KIND> using Int = CppTypeFor<TypeCategory::Integer, KIND>;
template <int KIND> using Real = CppTypeFor<TypeCategory::Real, KIND>;
@@ -43,25 +44,25 @@ TEST(Numeric, Exponent) {
EXPECT_EQ(RTNAME(Exponent4_4)(Real<4>{0}), 0);
EXPECT_EQ(RTNAME(Exponent4_8)(Real<4>{1.0}), 1);
EXPECT_EQ(RTNAME(Exponent8_4)(Real<8>{4.1}), 3);
- EXPECT_EQ(RTNAME(Exponent8_8)(std::numeric_limits<Real<8>>::infinity()),
- std::numeric_limits<Int<8>>::max());
- EXPECT_EQ(RTNAME(Exponent8_8)(std::numeric_limits<Real<8>>::quiet_NaN()),
- std::numeric_limits<Int<8>>::max());
+ EXPECT_EQ(RTNAME(Exponent8_8)(numeric_limits<Real<8>>::infinity()),
+ numeric_limits<Int<8>>::max());
+ EXPECT_EQ(RTNAME(Exponent8_8)(numeric_limits<Real<8>>::quiet_NaN()),
+ numeric_limits<Int<8>>::max());
}
TEST(Numeric, Fraction) {
EXPECT_EQ(RTNAME(Fraction4)(Real<4>{0}), 0);
EXPECT_EQ(RTNAME(Fraction8)(Real<8>{3.0}), 0.75);
EXPECT_TRUE(
- std::isnan(RTNAME(Fraction4)(std::numeric_limits<Real<4>>::infinity())));
+ std::isnan(RTNAME(Fraction4)(numeric_limits<Real<4>>::infinity())));
EXPECT_TRUE(
- std::isnan(RTNAME(Fraction8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+ std::isnan(RTNAME(Fraction8)(numeric_limits<Real<8>>::quiet_NaN())));
}
TEST(Numeric, IsNaN) {
EXPECT_FALSE(RTNAME(IsNaN4)(Real<4>{0}));
- EXPECT_FALSE(RTNAME(IsNaN8)(std::numeric_limits<Real<8>>::infinity()));
- EXPECT_TRUE(RTNAME(IsNaN8)(std::numeric_limits<Real<8>>::quiet_NaN()));
+ EXPECT_FALSE(RTNAME(IsNaN8)(numeric_limits<Real<8>>::infinity()));
+ EXPECT_TRUE(RTNAME(IsNaN8)(numeric_limits<Real<8>>::quiet_NaN()));
}
TEST(Numeric, Mod) {
@@ -74,28 +75,26 @@ TEST(Numeric, Mod) {
EXPECT_EQ(RTNAME(ModReal8)(Real<8>{8.0}, Real<8>(-5.0)), 3.0);
EXPECT_EQ(RTNAME(ModReal8)(Real<8>{-8.0}, Real<8>(-5.0)), -3.0);
EXPECT_EQ(
- RTNAME(ModReal4)(Real<4>{0.5}, std::numeric_limits<Real<4>>::infinity()),
- 0.5);
+ RTNAME(ModReal4)(Real<4>{0.5}, numeric_limits<Real<4>>::infinity()), 0.5);
EXPECT_EQ(
- RTNAME(ModReal4)(Real<4>{-0.5}, std::numeric_limits<Real<4>>::infinity()),
+ RTNAME(ModReal4)(Real<4>{-0.5}, numeric_limits<Real<4>>::infinity()),
-0.5);
EXPECT_EQ(
- RTNAME(ModReal4)(Real<4>{0.5}, -std::numeric_limits<Real<4>>::infinity()),
+ RTNAME(ModReal4)(Real<4>{0.5}, -numeric_limits<Real<4>>::infinity()),
0.5);
- EXPECT_EQ(RTNAME(ModReal4)(
- Real<4>{-0.5}, -std::numeric_limits<Real<4>>::infinity()),
+ EXPECT_EQ(
+ RTNAME(ModReal4)(Real<4>{-0.5}, -numeric_limits<Real<4>>::infinity()),
-0.5);
EXPECT_EQ(
- RTNAME(ModReal8)(Real<8>{0.5}, std::numeric_limits<Real<8>>::infinity()),
- 0.5);
+ RTNAME(ModReal8)(Real<8>{0.5}, numeric_limits<Real<8>>::infinity()), 0.5);
EXPECT_EQ(
- RTNAME(ModReal8)(Real<8>{-0.5}, std::numeric_limits<Real<8>>::infinity()),
+ RTNAME(ModReal8)(Real<8>{-0.5}, numeric_limits<Real<8>>::infinity()),
-0.5);
EXPECT_EQ(
- RTNAME(ModReal8)(Real<8>{0.5}, -std::numeric_limits<Real<8>>::infinity()),
+ RTNAME(ModReal8)(Real<8>{0.5}, -numeric_limits<Real<8>>::infinity()),
0.5);
- EXPECT_EQ(RTNAME(ModReal8)(
- Real<8>{-0.5}, -std::numeric_limits<Real<8>>::infinity()),
+ EXPECT_EQ(
+ RTNAME(ModReal8)(Real<8>{-0.5}, -numeric_limits<Real<8>>::infinity()),
-0.5);
}
@@ -109,22 +108,22 @@ TEST(Numeric, Modulo) {
EXPECT_EQ(RTNAME(ModuloReal8)(Real<8>{8.0}, Real<8>(-5.0)), -2.0);
EXPECT_EQ(RTNAME(ModuloReal8)(Real<8>{-8.0}, Real<8>(-5.0)), -3.0);
// MODULO(x, INF) == NaN
+ EXPECT_TRUE(std::isnan(
+ RTNAME(ModuloReal4)(Real<4>{0.5}, numeric_limits<Real<4>>::infinity())));
+ EXPECT_TRUE(std::isnan(
+ RTNAME(ModuloReal4)(Real<4>{-0.5}, numeric_limits<Real<4>>::infinity())));
+ EXPECT_TRUE(std::isnan(
+ RTNAME(ModuloReal4)(Real<4>{0.5}, -numeric_limits<Real<4>>::infinity())));
EXPECT_TRUE(std::isnan(RTNAME(ModuloReal4)(
- Real<4>{0.5}, std::numeric_limits<Real<4>>::infinity())));
- EXPECT_TRUE(std::isnan(RTNAME(ModuloReal4)(
- Real<4>{-0.5}, std::numeric_limits<Real<4>>::infinity())));
- EXPECT_TRUE(std::isnan(RTNAME(ModuloReal4)(
- Real<4>{0.5}, -std::numeric_limits<Real<4>>::infinity())));
- EXPECT_TRUE(std::isnan(RTNAME(ModuloReal4)(
- Real<4>{-0.5}, -std::numeric_limits<Real<4>>::infinity())));
- EXPECT_TRUE(std::isnan(RTNAME(ModuloReal8)(
- Real<8>{-0.5}, std::numeric_limits<Real<8>>::infinity())));
- EXPECT_TRUE(std::isnan(RTNAME(ModuloReal8)(
- Real<8>{0.5}, std::numeric_limits<Real<8>>::infinity())));
- EXPECT_TRUE(std::isnan(RTNAME(ModuloReal8)(
- Real<8>{-0.5}, -std::numeric_limits<Real<8>>::infinity())));
+ Real<4>{-0.5}, -numeric_limits<Real<4>>::infinity())));
+ EXPECT_TRUE(std::isnan(
+ RTNAME(ModuloReal8)(Real<8>{-0.5}, numeric_limits<Real<8>>::infinity())));
+ EXPECT_TRUE(std::isnan(
+ RTNAME(ModuloReal8)(Real<8>{0.5}, numeric_limits<Real<8>>::infinity())));
EXPECT_TRUE(std::isnan(RTNAME(ModuloReal8)(
- Real<8>{0.5}, -std::numeric_limits<Real<8>>::infinity())));
+ Real<8>{-0.5}, -numeric_limits<Real<8>>::infinity())));
+ EXPECT_TRUE(std::isnan(
+ RTNAME(ModuloReal8)(Real<8>{0.5}, -numeric_limits<Real<8>>::infinity())));
// MODULO(x, y) for integer values of x and y with 0 remainder.
EXPECT_EQ(RTNAME(ModuloReal4)(Real<4>{5.0}, Real<4>(1.0)), 0.0);
EXPECT_EQ(RTNAME(ModuloReal4)(Real<4>{5.0}, Real<4>(-1.0)), -0.0);
@@ -134,7 +133,7 @@ TEST(Numeric, Modulo) {
TEST(Numeric, Nearest) {
EXPECT_EQ(RTNAME(Nearest4)(Real<4>{0}, true),
- std::numeric_limits<Real<4>>::denorm_min());
+ numeric_limits<Real<4>>::denorm_min());
EXPECT_EQ(RTNAME(Nearest4)(Real<4>{3.0}, true),
Real<4>{3.0} + std::ldexp(Real<4>{1.0}, -22));
EXPECT_EQ(RTNAME(Nearest8)(Real<8>{1.0}, true),
@@ -156,9 +155,9 @@ TEST(Numeric, RRSpacing) {
EXPECT_EQ(RTNAME(RRSpacing4)(Real<4>{-3.0}), 0.75 * (1 << 24));
EXPECT_EQ(RTNAME(RRSpacing8)(Real<8>{-3.0}), 0.75 * (std::int64_t{1} << 53));
EXPECT_TRUE(
- std::isnan(RTNAME(RRSpacing4)(std::numeric_limits<Real<4>>::infinity())));
- EXPECT_TRUE(std::isnan(
- RTNAME(RRSpacing8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+ std::isnan(RTNAME(RRSpacing4)(numeric_limits<Real<4>>::infinity())));
+ EXPECT_TRUE(
+ std::isnan(RTNAME(RRSpacing8)(numeric_limits<Real<8>>::quiet_NaN())));
}
TEST(Numeric, Scale) {
@@ -167,9 +166,9 @@ TEST(Numeric, Scale) {
EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, 1), 2.0);
EXPECT_EQ(RTNAME(Scale4)(Real<4>{1.0}, -1), 0.5);
EXPECT_TRUE(
- std::isinf(RTNAME(Scale4)(std::numeric_limits<Real<4>>::infinity(), 1)));
+ std::isinf(RTNAME(Scale4)(numeric_limits<Real<4>>::infinity(), 1)));
EXPECT_TRUE(
- std::isnan(RTNAME(Scale8)(std::numeric_limits<Real<8>>::quiet_NaN(), 1)));
+ std::isnan(RTNAME(Scale8)(numeric_limits<Real<8>>::quiet_NaN(), 1)));
}
TEST(Numeric, SetExponent) {
@@ -179,10 +178,10 @@ TEST(Numeric, SetExponent) {
EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, 0), 0.5);
EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, 1), 1.0);
EXPECT_EQ(RTNAME(SetExponent4)(Real<4>{1.0}, -1), 0.25);
+ EXPECT_TRUE(
+ std::isnan(RTNAME(SetExponent4)(numeric_limits<Real<4>>::infinity(), 1)));
EXPECT_TRUE(std::isnan(
- RTNAME(SetExponent4)(std::numeric_limits<Real<4>>::infinity(), 1)));
- EXPECT_TRUE(std::isnan(
- RTNAME(SetExponent8)(std::numeric_limits<Real<8>>::quiet_NaN(), 1)));
+ RTNAME(SetExponent8)(numeric_limits<Real<8>>::quiet_NaN(), 1)));
}
TEST(Numeric, SelectedIntKind) {
@@ -253,17 +252,16 @@ TEST(Numeric, SelectedRealKind) {
}
TEST(Numeric, Spacing) {
- EXPECT_EQ(RTNAME(Spacing8)(Real<8>{0}), std::numeric_limits<Real<8>>::min());
+ EXPECT_EQ(RTNAME(Spacing8)(Real<8>{0}), numeric_limits<Real<8>>::min());
EXPECT_EQ(RTNAME(Spacing4)(Real<4>{3.0}), std::ldexp(Real<4>{1.0}, -22));
EXPECT_TRUE(
- std::isnan(RTNAME(Spacing4)(std::numeric_limits<Real<4>>::infinity())));
+ std::isnan(RTNAME(Spacing4)(numeric_limits<Real<4>>::infinity())));
EXPECT_TRUE(
- std::isnan(RTNAME(Spacing8)(std::numeric_limits<Real<8>>::quiet_NaN())));
+ std::isnan(RTNAME(Spacing8)(numeric_limits<Real<8>>::quiet_NaN())));
EXPECT_EQ(RTNAME(Spacing2By4)(Real<4>{3.0}), std::ldexp(Real<4>{1.0}, -9));
EXPECT_EQ(RTNAME(Spacing2By4)(Real<4>{0.0}), Real<4>{0.00006103515625E-04});
EXPECT_EQ(RTNAME(Spacing3By4)(Real<4>{3.0}), std::ldexp(Real<4>{1.0}, -6));
- EXPECT_EQ(
- RTNAME(Spacing3By4)(Real<4>{0.0}), std::numeric_limits<Real<4>>::min());
+ EXPECT_EQ(RTNAME(Spacing3By4)(Real<4>{0.0}), numeric_limits<Real<4>>::min());
}
TEST(Numeric, FPowI) {
@@ -334,7 +332,7 @@ TEST(Numeric, FPowI) {
double result;
*reinterpret_cast<std::uint64_t *>(&result) = 4607182414505051137ULL;
EXPECT_TRUE(std::abs(RTNAME(FPow8i)(Real<8>{base},
- Int<4>{std::numeric_limits<Int<4>>::min()}) -
+ Int<4>{numeric_limits<Int<4>>::min()}) -
Real<8>{result}) < 0.00000000001);
// (0x3FF0000000000001 ** 4294967296ULL) ~ 0x3FF00001000007FF
diff --git a/flang-rt/unittests/Runtime/Transformational.cpp b/flang-rt/unittests/Runtime/Transformational.cpp
index e61f5a64fcdb08..b883aefc1f85dc 100644
--- a/flang-rt/unittests/Runtime/Transformational.cpp
+++ b/flang-rt/unittests/Runtime/Transformational.cpp
@@ -11,6 +11,7 @@
#include "gtest/gtest.h"
#include "flang-rt/runtime/type-code.h"
#include "flang/Common/float128.h"
+#include "flang/Common/uint128.h"
#include <vector>
using namespace Fortran::runtime;
@@ -26,8 +27,8 @@ using BesselX0FuncType =
std::function<void(Descriptor &, int32_t, int32_t, const char *, int)>;
template <int KIND>
-constexpr CppTypeFor<TypeCategory::Real, KIND>
- besselEpsilon = CppTypeFor<TypeCategory::Real, KIND>(1e-4);
+constexpr CppTypeFor<TypeCategory::Real, KIND> besselEpsilon =
+ CppTypeFor<TypeCategory::Real, KIND>(1e-4);
template <int KIND>
static void testBesselJn(BesselFuncType<KIND> rtFunc, int32_t n1, int32_t n2,
@@ -176,7 +177,7 @@ static void testBesselYnX0(
EXPECT_EQ(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
j)),
- (-std::numeric_limits<
+ (-Fortran::common::numeric_limits<
CppTypeFor<TypeCategory::Real, KIND>>::infinity()));
}
}
diff --git a/flang/include/flang/Common/erfc-scaled.h b/flang/include/flang/Common/erfc-scaled.h
index 001af66baf34a4..9be61de162f180 100644
--- a/flang/include/flang/Common/erfc-scaled.h
+++ b/flang/include/flang/Common/erfc-scaled.h
@@ -39,7 +39,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
constexpr T sqrtpi{1.7724538509078120380404576221783883301349L};
constexpr T rsqrtpi{0.5641895835477562869480794515607725858440L};
- constexpr T epsilonby2{std::numeric_limits<T>::epsilon() * 0.5};
+ constexpr T epsilonby2{common::numeric_limits<T>::epsilon() * 0.5};
constexpr T xneg{-26.628e0};
constexpr T xhuge{6.71e7};
constexpr T thresh{0.46875e0};
@@ -47,7 +47,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
constexpr T one{1.0};
constexpr T four{4.0};
constexpr T sixteen{16.0};
- constexpr T xmax{1.0 / (sqrtpi * std::numeric_limits<T>::min())};
+ constexpr T xmax{1.0 / (sqrtpi * common::numeric_limits<T>::min())};
static_assert(xmax > xhuge, "xmax must be greater than xhuge");
T ysq;
@@ -106,7 +106,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
// fix up for negative argument, erf, etc.
if (x < zero) {
if (x < xneg) {
- result = std::numeric_limits<T>::max();
+ result = common::numeric_limits<T>::max();
} else {
ysq = trunc(x * sixteen) / sixteen;
del = (x - ysq) * (x + ysq);
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index c4bc4689a1eaae..4903ddc56558e7 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -22,30 +22,39 @@
#include "api-attrs.h"
#include "leading-zero-bit-count.h"
#include <cstdint>
+#include <limits>
#include <type_traits>
namespace Fortran::common {
+namespace detail {
+template <typename T> class numeric_limits;
+}
+
+/// Same as std::numeric_limits, but also defined for
+/// UnsignedInt128/SignedInt128.
+/// While std::numeric_limits is allowed to be extended for user-defined types
+/// (C++ [namespace.std]), it is not for the MSVC STL __int128/__uint128
+/// workaround below.
+template <typename T>
+using numeric_limits = std::conditional_t<std::is_arithmetic_v<T>,
+ std::numeric_limits<T>, detail::numeric_limits<T>>;
template <bool IS_SIGNED = false> class Int128 {
+ friend class detail::numeric_limits<Int128>;
+
public:
constexpr Int128() {}
// This means of definition provides some portability for
// "size_t" operands.
- constexpr Int128(unsigned n) : low_{n} {}
- constexpr Int128(unsigned long n) : low_{n} {}
- constexpr Int128(unsigned long long n) : low_{n} {}
- constexpr Int128(int n) {
+ template <typename T,
+ typename = std::enable_if_t<std::is_integral_v<T> && sizeof(T) <= 8>>
+ constexpr Int128(T n) {
low_ = static_cast<std::uint64_t>(n);
- high_ = -static_cast<std::uint64_t>(n < 0);
- }
- constexpr Int128(long n) {
- low_ = static_cast<std::uint64_t>(n);
- high_ = -static_cast<std::uint64_t>(n < 0);
- }
- constexpr Int128(long long n) {
- low_ = static_cast<std::uint64_t>(n);
- high_ = -static_cast<std::uint64_t>(n < 0);
+ if constexpr (std::is_signed_v<T>) {
+ high_ = -static_cast<std::uint64_t>(n < 0);
+ }
}
+
constexpr Int128(const Int128 &) = default;
constexpr Int128(Int128 &&) = default;
constexpr Int128 &operator=(const Int128 &) = default;
@@ -61,9 +70,12 @@ template <bool IS_SIGNED = false> class Int128 {
constexpr Int128 operator-() const { return ~*this + 1; }
constexpr bool operator!() const { return !low_ && !high_; }
constexpr explicit operator bool() const { return low_ || high_; }
- constexpr explicit operator std::uint64_t() const { return low_; }
- constexpr explicit operator std::int64_t() const { return low_; }
- constexpr explicit operator int() const { return static_cast<int>(low_); }
+
+ template <typename T,
+ typename = std::enable_if_t<std::is_integral_v<T> && sizeof(T) <= 8>>
+ constexpr explicit operator T() const {
+ return static_cast<T>(low_);
+ }
constexpr std::uint64_t high() const { return high_; }
constexpr std::uint64_t low() const { return low_; }
@@ -304,5 +316,72 @@ using HostUnsignedIntType = typename HostUnsignedIntTypeHelper<BITS>::type;
template <int BITS>
using HostSignedIntType = typename HostSignedIntTypeHelper<BITS>::type;
+namespace detail {
+
+template <> class numeric_limits<Fortran::common::UnsignedInt128> {
+public:
+ using T = Fortran::common::UnsignedInt128;
+
+ static constexpr bool is_specialized{true};
+ static constexpr bool is_signed{false};
+ static constexpr bool is_integer{true};
+
+ static constexpr T min() { return T{0, 0}; }
+ static constexpr T max() { return T{UINT64_MAX, UINT64_MAX}; }
+ static constexpr T lowest() { return min(); }
+};
+
+template <> class numeric_limits<Fortran::common::SignedInt128> {
+public:
+ using T = Fortran::common::SignedInt128;
+
+ static constexpr bool is_specialized{true};
+ static constexpr bool is_signed{true};
+ static constexpr bool is_integer{true};
+
+ static constexpr T min() {
+ return T{static_cast<std::uint64_t>(INT64_MIN), 0};
+ }
+ static constexpr T max() {
+ return T{static_cast<std::uint64_t>(INT64_MAX), UINT64_MAX};
+ }
+ static constexpr T lowest() { return min(); }
+};
+
+#if defined(__SIZEOF_INT128__) && defined(_MSVC_STL_VERSION)
+// clang-cl knows __int128 and will be used for (u)int128_t, but the MSVC STL
+// does not define stl::numeric_limits for it.
+
+template <> class numeric_limits<unsigned __int128> {
+public:
+ using T = unsigned __int128;
+
+ static constexpr bool is_specialized{true};
+ static constexpr bool is_signed{false};
+ static constexpr bool is_integer{true};
+
+ static constexpr T min() { return static_cast<T>(0); }
+ static constexpr T max() { return ~static_cast<T>(0); }
+ static constexpr T lowest() { return min(); }
+};
+
+template <> class numeric_limits<__int128> {
+public:
+ using T = __int128;
+
+ static constexpr bool is_specialized{true};
+ static constexpr bool is_signed{true};
+ static constexpr bool is_integer{true};
+
+ static constexpr T min() {
+ return static_cast<T>(static_cast<unsigned __int128>(1) << 127u);
+ }
+ static constexpr T max() {
+ return static_cast<T>(~(static_cast<unsigned __int128>(1) << 127u));
+ }
+ static constexpr T lowest() { return min(); }
+};
+#endif
+} // namespace detail
} // namespace Fortran::common
#endif
diff --git a/flang/lib/Decimal/big-radix-floating-point.h b/flang/lib/Decimal/big-radix-floating-point.h
index 63890f271f6e55..22d38d69676358 100644
--- a/flang/lib/Decimal/big-radix-floating-point.h
+++ b/flang/lib/Decimal/big-radix-floating-point.h
@@ -53,9 +53,9 @@ template <int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
64 - common::LeadingZeroBitCount(uint64Radix)};
using Digit = common::HostUnsignedIntType<minDigitBits>;
static constexpr Digit radix{uint64Radix};
- static_assert(radix < std::numeric_limits<Digit>::max() / 1000,
+ static_assert(radix < common::numeric_limits<Digit>::max() / 1000,
"radix is somehow too big");
- static_assert(radix > std::numeric_limits<Digit>::max() / 10000,
+ static_assert(radix > common::numeric_limits<Digit>::max() / 10000,
"radix is somehow too small");
// The base-2 logarithm of the least significant bit that can arise
diff --git a/flang/lib/Evaluate/constant.cpp b/flang/lib/Evaluate/constant.cpp
index 7fe000892ac1a2..d0552dc4d76da6 100644
--- a/flang/lib/Evaluate/constant.cpp
+++ b/flang/lib/Evaluate/constant.cpp
@@ -87,7 +87,7 @@ std::optional<uint64_t> TotalElementCount(const ConstantSubscripts &shape) {
CHECK(dim >= 0);
uint64_t osize{size};
size = osize * dim;
- if (size > std::numeric_limits<decltype(dim)>::max() ||
+ if (size > common::numeric_limits<decltype(dim)>::max() ||
(dim != 0 && size / dim != osize)) {
return std::nullopt;
}
>From 16bd62a88d1b1d4d8cf23df820018197cf75101f Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Thu, 1 Oct 2026 17:22:53 +0200
Subject: [PATCH 02/15] Remove unrelated changes
---
flang/include/flang/Common/uint128.h | 29 +++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index 4903ddc56558e7..869b64f08c1f78 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -46,15 +46,21 @@ template <bool IS_SIGNED = false> class Int128 {
constexpr Int128() {}
// This means of definition provides some portability for
// "size_t" operands.
- template <typename T,
- typename = std::enable_if_t<std::is_integral_v<T> && sizeof(T) <= 8>>
- constexpr Int128(T n) {
+ constexpr Int128(unsigned n) : low_{n} {}
+ constexpr Int128(unsigned long n) : low_{n} {}
+ constexpr Int128(unsigned long long n) : low_{n} {}
+ constexpr Int128(int n) {
low_ = static_cast<std::uint64_t>(n);
- if constexpr (std::is_signed_v<T>) {
- high_ = -static_cast<std::uint64_t>(n < 0);
- }
+ high_ = -static_cast<std::uint64_t>(n < 0);
+ }
+ constexpr Int128(long n) {
+ low_ = static_cast<std::uint64_t>(n);
+ high_ = -static_cast<std::uint64_t>(n < 0);
+ }
+ constexpr Int128(long long n) {
+ low_ = static_cast<std::uint64_t>(n);
+ high_ = -static_cast<std::uint64_t>(n < 0);
}
-
constexpr Int128(const Int128 &) = default;
constexpr Int128(Int128 &&) = default;
constexpr Int128 &operator=(const Int128 &) = default;
@@ -70,12 +76,9 @@ template <bool IS_SIGNED = false> class Int128 {
constexpr Int128 operator-() const { return ~*this + 1; }
constexpr bool operator!() const { return !low_ && !high_; }
constexpr explicit operator bool() const { return low_ || high_; }
-
- template <typename T,
- typename = std::enable_if_t<std::is_integral_v<T> && sizeof(T) <= 8>>
- constexpr explicit operator T() const {
- return static_cast<T>(low_);
- }
+ constexpr explicit operator std::uint64_t() const { return low_; }
+ constexpr explicit operator std::int64_t() const { return low_; }
+ constexpr explicit operator int() const { return static_cast<int>(low_); }
constexpr std::uint64_t high() const { return high_; }
constexpr std::uint64_t low() const { return low_; }
>From 68b6e01f9ae33ee0a86a2c26470f54822fd1c4b4 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Fri, 2 Oct 2026 14:26:45 +0200
Subject: [PATCH 03/15] Handle strict mode libstdc++ as well
Avoid warning
Add unittests
HAS_NATIVE_UINT128_T only in unittest
clang-format
---
flang/include/flang/Common/uint128.h | 34 +++++++++++++++++++---------
flang/unittests/Evaluate/uint128.cpp | 30 ++++++++++++++++++++++++
2 files changed, 53 insertions(+), 11 deletions(-)
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index 869b64f08c1f78..a00d8c953bb7c7 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -36,8 +36,9 @@ template <typename T> class numeric_limits;
/// (C++ [namespace.std]), it is not for the MSVC STL __int128/__uint128
/// workaround below.
template <typename T>
-using numeric_limits = std::conditional_t<std::is_arithmetic_v<T>,
- std::numeric_limits<T>, detail::numeric_limits<T>>;
+using numeric_limits =
+ std::conditional_t<std::numeric_limits<T>::is_specialized,
+ std::numeric_limits<T>, detail::numeric_limits<T>>;
template <bool IS_SIGNED = false> class Int128 {
friend class detail::numeric_limits<Int128>;
@@ -351,13 +352,24 @@ template <> class numeric_limits<Fortran::common::SignedInt128> {
static constexpr T lowest() { return min(); }
};
-#if defined(__SIZEOF_INT128__) && defined(_MSVC_STL_VERSION)
-// clang-cl knows __int128 and will be used for (u)int128_t, but the MSVC STL
-// does not define stl::numeric_limits for it.
+#if defined(__SIZEOF_INT128__)
+// Handle discrepancy of support bit 128 bit integers by compiler and standard
+// library. The compiler may treat __int128 as a builtin type, but the standard
+// library does not define std::numeric_limits for it. Two cases are known:
+//
+// 1. clang-cl supports __int128, but MSVC, and therefore its STL used by
+// clang-cl, does not.
+//
+// 2. libstdc++ in strict mode (-std=c++NN) intentionally removes any use of
+// __int128, even though gcc does not make such a distinction.
+//
+// Note: Using __int128_t/__uint128_t typedefs; spelling out the __int128
+// keyword is a warning "ISO C++ does not support ‘__int128’ for ‘type name’"
+// under -Wpedantic
-template <> class numeric_limits<unsigned __int128> {
+template <> class numeric_limits<__uint128_t> {
public:
- using T = unsigned __int128;
+ using T = __uint128_t;
static constexpr bool is_specialized{true};
static constexpr bool is_signed{false};
@@ -368,19 +380,19 @@ template <> class numeric_limits<unsigned __int128> {
static constexpr T lowest() { return min(); }
};
-template <> class numeric_limits<__int128> {
+template <> class numeric_limits<__int128_t> {
public:
- using T = __int128;
+ using T = __int128_t;
static constexpr bool is_specialized{true};
static constexpr bool is_signed{true};
static constexpr bool is_integer{true};
static constexpr T min() {
- return static_cast<T>(static_cast<unsigned __int128>(1) << 127u);
+ return static_cast<T>(static_cast<__uint128_t>(1) << 127u);
}
static constexpr T max() {
- return static_cast<T>(~(static_cast<unsigned __int128>(1) << 127u));
+ return static_cast<T>(~(static_cast<__uint128_t>(1) << 127u));
}
static constexpr T lowest() { return min(); }
};
diff --git a/flang/unittests/Evaluate/uint128.cpp b/flang/unittests/Evaluate/uint128.cpp
index 0b749abe1c080a..6c557c8a57d6cd 100644
--- a/flang/unittests/Evaluate/uint128.cpp
+++ b/flang/unittests/Evaluate/uint128.cpp
@@ -3,6 +3,7 @@
#include "flang/Testing/testing.h"
#include "llvm/Support/raw_ostream.h"
#include <cinttypes>
+#include <limits>
#if (defined __GNUC__ || defined __clang__) && defined __SIZEOF_INT128__
#define HAS_NATIVE_UINT128_T 1
@@ -113,6 +114,33 @@ static void TestVsNative() {
}
#endif
+template <typename T> static void TestNumericLimitsEquivalence() {
+ using Std = std::numeric_limits<T>;
+ using Detail = Fortran::common::detail::numeric_limits<T>;
+ using Common = Fortran::common::numeric_limits<T>;
+
+ // Out implementation in Detail must be equivalent to Std (if specialized).
+ if constexpr (Std::is_specialized) {
+ TEST(Detail::is_specialized == Std::is_specialized);
+ TEST(Detail::is_signed == Std::is_signed);
+ TEST(Detail::is_integer == Std::is_integer);
+ TEST(Detail::min() == Std::min());
+ TEST(Detail::max() == Std::max());
+ TEST(Detail::lowest() == Std::lowest());
+ } else {
+ llvm::outs() << "std::numeric_limits is not specialized for this type\n";
+ }
+
+ // Common may refer to either Std to Detail; in either case it must be
+ // identical.
+ TEST(Detail::is_specialized == Common::is_specialized);
+ TEST(Detail::is_integer == Common::is_integer);
+ TEST(Detail::is_signed == Common::is_signed);
+ TEST(Detail::min() == Common::min());
+ TEST(Detail::max() == Common::max());
+ TEST(Detail::lowest() == Common::lowest());
+}
+
int main() {
for (std::uint64_t j{0}; j < 64; ++j) {
Test(j);
@@ -125,6 +153,8 @@ int main() {
#if HAS_NATIVE_UINT128_T
llvm::outs() << "Environment has native __uint128_t\n";
TestVsNative();
+ TestNumericLimitsEquivalence<__int128_t>();
+ TestNumericLimitsEquivalence<__uint128_t>();
#else
llvm::outs() << "Environment lacks native __uint128_t\n";
#endif
>From 54b21ecbce49b6d03724f7a5a33c4b59bf24638f Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Fri, 2 Oct 2026 15:16:48 +0200
Subject: [PATCH 04/15] Undo unrelated formatting change
---
flang-rt/unittests/Runtime/Transformational.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang-rt/unittests/Runtime/Transformational.cpp b/flang-rt/unittests/Runtime/Transformational.cpp
index b883aefc1f85dc..6ee299405c7065 100644
--- a/flang-rt/unittests/Runtime/Transformational.cpp
+++ b/flang-rt/unittests/Runtime/Transformational.cpp
@@ -27,8 +27,8 @@ using BesselX0FuncType =
std::function<void(Descriptor &, int32_t, int32_t, const char *, int)>;
template <int KIND>
-constexpr CppTypeFor<TypeCategory::Real, KIND> besselEpsilon =
- CppTypeFor<TypeCategory::Real, KIND>(1e-4);
+constexpr CppTypeFor<TypeCategory::Real, KIND>
+ besselEpsilon = CppTypeFor<TypeCategory::Real, KIND>(1e-4);
template <int KIND>
static void testBesselJn(BesselFuncType<KIND> rtFunc, int32_t n1, int32_t n2,
>From cefa9f8c99246547bccc90104ec386815fb4345f Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Sat, 3 Oct 2026 17:40:57 +0200
Subject: [PATCH 05/15] Avoid std::is_arithmetic_v, std::is_integral_v,
std::is_unsigned_t
---
flang-rt/lib/runtime/transformational.cpp | 2 +-
flang-rt/unittests/Runtime/Transformational.cpp | 3 +--
flang/lib/Decimal/big-radix-floating-point.h | 3 +--
flang/lib/Evaluate/fold-implementation.h | 2 +-
flang/lib/Semantics/openmp-utils.cpp | 6 ++++--
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/flang-rt/lib/runtime/transformational.cpp b/flang-rt/lib/runtime/transformational.cpp
index 4e6f24ed79e8c4..395721ca2b9bd9 100644
--- a/flang-rt/lib/runtime/transformational.cpp
+++ b/flang-rt/lib/runtime/transformational.cpp
@@ -320,7 +320,7 @@ static inline RT_API_ATTRS void DoBesselYnX0(Descriptor &result, int32_t n1,
for (int j{1}; j <= n2 - n1 + 1; ++j) {
at[0] = j;
*result.Element<CppTypeFor<CAT, KIND>>(at) =
- -common::numeric_limits<CppTypeFor<CAT, KIND>>::infinity();
+ -std::numeric_limits<CppTypeFor<CAT, KIND>>::infinity();
}
}
diff --git a/flang-rt/unittests/Runtime/Transformational.cpp b/flang-rt/unittests/Runtime/Transformational.cpp
index 6ee299405c7065..e61f5a64fcdb08 100644
--- a/flang-rt/unittests/Runtime/Transformational.cpp
+++ b/flang-rt/unittests/Runtime/Transformational.cpp
@@ -11,7 +11,6 @@
#include "gtest/gtest.h"
#include "flang-rt/runtime/type-code.h"
#include "flang/Common/float128.h"
-#include "flang/Common/uint128.h"
#include <vector>
using namespace Fortran::runtime;
@@ -177,7 +176,7 @@ static void testBesselYnX0(
EXPECT_EQ(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
j)),
- (-Fortran::common::numeric_limits<
+ (-std::numeric_limits<
CppTypeFor<TypeCategory::Real, KIND>>::infinity()));
}
}
diff --git a/flang/lib/Decimal/big-radix-floating-point.h b/flang/lib/Decimal/big-radix-floating-point.h
index 22d38d69676358..f775b6939df31d 100644
--- a/flang/lib/Decimal/big-radix-floating-point.h
+++ b/flang/lib/Decimal/big-radix-floating-point.h
@@ -149,8 +149,7 @@ template <int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
// Sets *this to an unsigned integer value.
// Returns any remainder.
template <typename UINT> RT_API_ATTRS UINT SetTo(UINT n) {
- static_assert(
- std::is_same_v<UINT, common::uint128_t> || std::is_unsigned_v<UINT>);
+ static_assert(!common::numeric_limits<UINT>::is_signed);
SetToZero();
while (n != 0) {
auto q{n / 10u};
diff --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index 03414f1e789e08..e775efa4450579 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -581,7 +581,7 @@ std::optional<std::int64_t> GetInt64ArgOr(
template <typename A, typename B>
std::optional<std::vector<A>> GetIntegerVector(const B &x) {
- static_assert(std::is_integral_v<A>);
+ static_assert(common::numeric_limits<A>::is_integer);
if (const auto *someInteger{UnwrapExpr<Expr<SomeInteger>>(x)}) {
return common::visit(
[](const auto &typedExpr) -> std::optional<std::vector<A>> {
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 794a748592bced..a3658af5e345e4 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1310,7 +1310,8 @@ bool IsTransparentInterveningCode(const parser::ExecutionPartConstruct &x) {
}
template <typename T,
- typename = std::enable_if_t<std::is_arithmetic_v<llvm::remove_cvref_t<T>>>>
+ typename = std::enable_if_t<
+ common::numeric_limits<llvm::remove_cvref_t<T>>::is_specialized>>
WithReason<T> operator+(const WithReason<T> &a, const WithReason<T> &b) {
if (a.value && b.value) {
return WithReason<T>{
@@ -1320,7 +1321,8 @@ WithReason<T> operator+(const WithReason<T> &a, const WithReason<T> &b) {
}
template <typename T,
- typename = std::enable_if_t<std::is_arithmetic_v<llvm::remove_cvref_t<T>>>>
+ typename = std::enable_if_t<
+ common::numeric_limits<llvm::remove_cvref_t<T>>::is_specialized>>
WithReason<T> operator+(T a, const WithReason<T> &b) {
return WithReason<T>{a, Reason()} + b;
}
>From 9c92c5c8e506b9389207f5281031a5d299ab9801 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Sun, 4 Oct 2026 05:29:52 +0200
Subject: [PATCH 06/15] Use for __float128
clang-format
---
flang-rt/lib/runtime/transformational.cpp | 2 +-
.../unittests/Runtime/Transformational.cpp | 2 +-
flang/include/flang/Common/uint128.h | 26 ++++++++++++-------
3 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/flang-rt/lib/runtime/transformational.cpp b/flang-rt/lib/runtime/transformational.cpp
index 395721ca2b9bd9..4e6f24ed79e8c4 100644
--- a/flang-rt/lib/runtime/transformational.cpp
+++ b/flang-rt/lib/runtime/transformational.cpp
@@ -320,7 +320,7 @@ static inline RT_API_ATTRS void DoBesselYnX0(Descriptor &result, int32_t n1,
for (int j{1}; j <= n2 - n1 + 1; ++j) {
at[0] = j;
*result.Element<CppTypeFor<CAT, KIND>>(at) =
- -std::numeric_limits<CppTypeFor<CAT, KIND>>::infinity();
+ -common::numeric_limits<CppTypeFor<CAT, KIND>>::infinity();
}
}
diff --git a/flang-rt/unittests/Runtime/Transformational.cpp b/flang-rt/unittests/Runtime/Transformational.cpp
index e61f5a64fcdb08..15af7133910144 100644
--- a/flang-rt/unittests/Runtime/Transformational.cpp
+++ b/flang-rt/unittests/Runtime/Transformational.cpp
@@ -176,7 +176,7 @@ static void testBesselYnX0(
EXPECT_EQ(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
j)),
- (-std::numeric_limits<
+ (-Fortran::common::numeric_limits<
CppTypeFor<TypeCategory::Real, KIND>>::infinity()));
}
}
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index a00d8c953bb7c7..d66a59d149d6f0 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -30,16 +30,6 @@ namespace detail {
template <typename T> class numeric_limits;
}
-/// Same as std::numeric_limits, but also defined for
-/// UnsignedInt128/SignedInt128.
-/// While std::numeric_limits is allowed to be extended for user-defined types
-/// (C++ [namespace.std]), it is not for the MSVC STL __int128/__uint128
-/// workaround below.
-template <typename T>
-using numeric_limits =
- std::conditional_t<std::numeric_limits<T>::is_specialized,
- std::numeric_limits<T>, detail::numeric_limits<T>>;
-
template <bool IS_SIGNED = false> class Int128 {
friend class detail::numeric_limits<Int128>;
@@ -322,6 +312,11 @@ using HostSignedIntType = typename HostSignedIntTypeHelper<BITS>::type;
namespace detail {
+template <typename T> class numeric_limits {
+public:
+ static constexpr bool is_specialized{false};
+};
+
template <> class numeric_limits<Fortran::common::UnsignedInt128> {
public:
using T = Fortran::common::UnsignedInt128;
@@ -398,5 +393,16 @@ template <> class numeric_limits<__int128_t> {
};
#endif
} // namespace detail
+
+/// Same as std::numeric_limits, but also defined for 128 bit integers. While
+/// std::numeric_limits is allowed to be extended for user-defined types such as
+/// UnsignedInt128/SignedInt128 (C++ [namespace.std]), it is not for the
+/// __int128/__uint128 workaround above.
+template <typename T>
+using numeric_limits =
+ std::conditional_t<detail::numeric_limits<T>::is_specialized &&
+ !std::numeric_limits<T>::is_specialized,
+ detail::numeric_limits<T>, std::numeric_limits<T>>;
+
} // namespace Fortran::common
#endif
>From 21239e2037d6132d2c41002285e9b5ab3c39e208 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Sun, 4 Oct 2026 15:05:01 +0200
Subject: [PATCH 07/15] Apply some AI suggestions
---
flang/include/flang/Common/erfc-scaled.h | 6 +++---
flang/include/flang/Common/uint128.h | 25 ++++++++++++++----------
flang/unittests/Evaluate/uint128.cpp | 5 ++++-
3 files changed, 22 insertions(+), 14 deletions(-)
diff --git a/flang/include/flang/Common/erfc-scaled.h b/flang/include/flang/Common/erfc-scaled.h
index 9be61de162f180..001af66baf34a4 100644
--- a/flang/include/flang/Common/erfc-scaled.h
+++ b/flang/include/flang/Common/erfc-scaled.h
@@ -39,7 +39,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
constexpr T sqrtpi{1.7724538509078120380404576221783883301349L};
constexpr T rsqrtpi{0.5641895835477562869480794515607725858440L};
- constexpr T epsilonby2{common::numeric_limits<T>::epsilon() * 0.5};
+ constexpr T epsilonby2{std::numeric_limits<T>::epsilon() * 0.5};
constexpr T xneg{-26.628e0};
constexpr T xhuge{6.71e7};
constexpr T thresh{0.46875e0};
@@ -47,7 +47,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
constexpr T one{1.0};
constexpr T four{4.0};
constexpr T sixteen{16.0};
- constexpr T xmax{1.0 / (sqrtpi * common::numeric_limits<T>::min())};
+ constexpr T xmax{1.0 / (sqrtpi * std::numeric_limits<T>::min())};
static_assert(xmax > xhuge, "xmax must be greater than xhuge");
T ysq;
@@ -106,7 +106,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
// fix up for negative argument, erf, etc.
if (x < zero) {
if (x < xneg) {
- result = common::numeric_limits<T>::max();
+ result = std::numeric_limits<T>::max();
} else {
ysq = trunc(x * sixteen) / sixteen;
del = (x - ysq) * (x + ysq);
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index d66a59d149d6f0..65cba4587d4cfc 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -348,17 +348,19 @@ template <> class numeric_limits<Fortran::common::SignedInt128> {
};
#if defined(__SIZEOF_INT128__)
-// Handle discrepancy of support bit 128 bit integers by compiler and standard
-// library. The compiler may treat __int128 as a builtin type, but the standard
-// library does not define std::numeric_limits for it. Two cases are known:
+// Handle discrepancy of support of bit 128 bit integers by compiler and
+// standard library. The compiler may treat __int128 as a builtin type, but the
+// standard library does not define std::numeric_limits for it. Two cases are
+// known:
//
// 1. clang-cl supports __int128, but MSVC, and therefore its STL used by
// clang-cl, does not.
//
-// 2. libstdc++ in strict mode (-std=c++NN) intentionally removes any use of
-// __int128, even though gcc does not make such a distinction.
+// 2. Some versions of libstdc++ in strict mode (-std=c++NN)
+// intentionally removes any use of __int128, even though gcc does not make
+// such a distinction.
//
-// Note: Using __int128_t/__uint128_t typedefs; spelling out the __int128
+// Using __int128_t/__uint128_t typedefs; spelling out the __int128
// keyword is a warning "ISO C++ does not support ‘__int128’ for ‘type name’"
// under -Wpedantic
@@ -398,11 +400,14 @@ template <> class numeric_limits<__int128_t> {
/// std::numeric_limits is allowed to be extended for user-defined types such as
/// UnsignedInt128/SignedInt128 (C++ [namespace.std]), it is not for the
/// __int128/__uint128 workaround above.
+///
+/// Only defining the members actually used in Flang/Flang-RT. Feel free to add
+/// more members as needed.
template <typename T>
-using numeric_limits =
- std::conditional_t<detail::numeric_limits<T>::is_specialized &&
- !std::numeric_limits<T>::is_specialized,
- detail::numeric_limits<T>, std::numeric_limits<T>>;
+using numeric_limits = std::conditional_t<
+ detail::numeric_limits<std::decay_t<T>>::is_specialized &&
+ !std::numeric_limits<T>::is_specialized,
+ detail::numeric_limits<std::decay_t<T>>, std::numeric_limits<T>>;
} // namespace Fortran::common
#endif
diff --git a/flang/unittests/Evaluate/uint128.cpp b/flang/unittests/Evaluate/uint128.cpp
index 6c557c8a57d6cd..4fa742a1487a7e 100644
--- a/flang/unittests/Evaluate/uint128.cpp
+++ b/flang/unittests/Evaluate/uint128.cpp
@@ -119,7 +119,8 @@ template <typename T> static void TestNumericLimitsEquivalence() {
using Detail = Fortran::common::detail::numeric_limits<T>;
using Common = Fortran::common::numeric_limits<T>;
- // Out implementation in Detail must be equivalent to Std (if specialized).
+ // Our implementation in Detail must be equivalent to Std (if Std supports
+ // __int128).
if constexpr (Std::is_specialized) {
TEST(Detail::is_specialized == Std::is_specialized);
TEST(Detail::is_signed == Std::is_signed);
@@ -155,6 +156,8 @@ int main() {
TestVsNative();
TestNumericLimitsEquivalence<__int128_t>();
TestNumericLimitsEquivalence<__uint128_t>();
+ TestNumericLimitsEquivalence<const __int128_t>();
+ TestNumericLimitsEquivalence<const __uint128_t>();
#else
llvm::outs() << "Environment lacks native __uint128_t\n";
#endif
>From c903e0ac854aa595c93c8f944685346b6e84cec1 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Sun, 4 Oct 2026 15:25:25 +0200
Subject: [PATCH 08/15] Add unittests
---
flang-rt/unittests/Runtime/Reduction.cpp | 34 ++++++++++++++++++++++++
flang/include/flang/Common/uint128.h | 26 ++++++++++--------
flang/unittests/Evaluate/uint128.cpp | 4 +--
3 files changed, 51 insertions(+), 13 deletions(-)
diff --git a/flang-rt/unittests/Runtime/Reduction.cpp b/flang-rt/unittests/Runtime/Reduction.cpp
index 5d7bb20e011ad2..50824cf19146e5 100644
--- a/flang-rt/unittests/Runtime/Reduction.cpp
+++ b/flang-rt/unittests/Runtime/Reduction.cpp
@@ -638,6 +638,40 @@ TEST(Reductions, ExtremaReal16) {
}
#endif // HAS_LDBL128 || HAS_FLOAT128
+#ifdef __SIZEOF_INT128__
+TEST(Reductions, ExtremaInteger16) {
+ // The identity value for Minval for INTEGER(16) was mistakenly
+ // set to -1 instead of HUGE(0_16).
+ using ElemType = CppTypeFor<TypeCategory::Integer, 16>;
+ const ElemType huge{
+ static_cast<ElemType>(~Fortran::common::uint128_t{0} >> 1)};
+ const ElemType lowest{-huge - 1};
+
+ // Zero-sized array
+ auto emptyArray{MakeArray<TypeCategory::Integer, 16>(
+ std::vector<int>{0}, std::vector<ElemType>{})};
+ EXPECT_TRUE(RTNAME(MinvalInteger16)(*emptyArray, __FILE__, __LINE__) == huge);
+ EXPECT_TRUE(
+ RTNAME(MaxvalInteger16)(*emptyArray, __FILE__, __LINE__) == lowest);
+
+ // All elements masked out
+ std::vector<int> shape{3};
+ // 1 2 3
+ auto array{MakeArray<TypeCategory::Integer, 16>(
+ shape, std::vector<ElemType>{1, 2, 3})};
+ auto falseMask{MakeArray<TypeCategory::Logical, 4>(
+ shape, std::vector<std::int32_t>{0, 0, 0})};
+ EXPECT_TRUE(RTNAME(MinvalInteger16)(
+ *array, __FILE__, __LINE__, /*DIM=*/0, &*falseMask) == huge);
+ EXPECT_TRUE(RTNAME(MaxvalInteger16)(*array, __FILE__, __LINE__, /*DIM=*/0,
+ &*falseMask) == lowest);
+
+ // Non-empty
+ EXPECT_TRUE(RTNAME(MinvalInteger16)(*array, __FILE__, __LINE__) == 1);
+ EXPECT_TRUE(RTNAME(MaxvalInteger16)(*array, __FILE__, __LINE__) == 3);
+}
+#endif // __SIZEOF_INT128__
+
static std::int32_t IAdd(const std::int32_t *x, const std::int32_t *y) {
return *x + *y;
}
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index 65cba4587d4cfc..f53cb81778c1a6 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -27,11 +27,11 @@
namespace Fortran::common {
namespace detail {
-template <typename T> class numeric_limits;
+template <typename T> class numeric_limits_impl;
}
template <bool IS_SIGNED = false> class Int128 {
- friend class detail::numeric_limits<Int128>;
+ friend class detail::numeric_limits_impl<Int128>;
public:
constexpr Int128() {}
@@ -312,12 +312,12 @@ using HostSignedIntType = typename HostSignedIntTypeHelper<BITS>::type;
namespace detail {
-template <typename T> class numeric_limits {
+template <typename T> class numeric_limits_impl {
public:
static constexpr bool is_specialized{false};
};
-template <> class numeric_limits<Fortran::common::UnsignedInt128> {
+template <> class numeric_limits_impl<Fortran::common::UnsignedInt128> {
public:
using T = Fortran::common::UnsignedInt128;
@@ -330,7 +330,7 @@ template <> class numeric_limits<Fortran::common::UnsignedInt128> {
static constexpr T lowest() { return min(); }
};
-template <> class numeric_limits<Fortran::common::SignedInt128> {
+template <> class numeric_limits_impl<Fortran::common::SignedInt128> {
public:
using T = Fortran::common::SignedInt128;
@@ -364,7 +364,7 @@ template <> class numeric_limits<Fortran::common::SignedInt128> {
// keyword is a warning "ISO C++ does not support ‘__int128’ for ‘type name’"
// under -Wpedantic
-template <> class numeric_limits<__uint128_t> {
+template <> class numeric_limits_impl<__uint128_t> {
public:
using T = __uint128_t;
@@ -377,7 +377,7 @@ template <> class numeric_limits<__uint128_t> {
static constexpr T lowest() { return min(); }
};
-template <> class numeric_limits<__int128_t> {
+template <> class numeric_limits_impl<__int128_t> {
public:
using T = __int128_t;
@@ -394,6 +394,10 @@ template <> class numeric_limits<__int128_t> {
static constexpr T lowest() { return min(); }
};
#endif
+
+template <typename T>
+using numeric_limits = numeric_limits_impl<std::remove_cv_t<T>>;
+
} // namespace detail
/// Same as std::numeric_limits, but also defined for 128 bit integers. While
@@ -404,10 +408,10 @@ template <> class numeric_limits<__int128_t> {
/// Only defining the members actually used in Flang/Flang-RT. Feel free to add
/// more members as needed.
template <typename T>
-using numeric_limits = std::conditional_t<
- detail::numeric_limits<std::decay_t<T>>::is_specialized &&
- !std::numeric_limits<T>::is_specialized,
- detail::numeric_limits<std::decay_t<T>>, std::numeric_limits<T>>;
+using numeric_limits =
+ std::conditional_t<detail::numeric_limits<T>::is_specialized &&
+ !std::numeric_limits<T>::is_specialized,
+ detail::numeric_limits<T>, std::numeric_limits<T>>;
} // namespace Fortran::common
#endif
diff --git a/flang/unittests/Evaluate/uint128.cpp b/flang/unittests/Evaluate/uint128.cpp
index 4fa742a1487a7e..00e65adea2bbd4 100644
--- a/flang/unittests/Evaluate/uint128.cpp
+++ b/flang/unittests/Evaluate/uint128.cpp
@@ -119,8 +119,8 @@ template <typename T> static void TestNumericLimitsEquivalence() {
using Detail = Fortran::common::detail::numeric_limits<T>;
using Common = Fortran::common::numeric_limits<T>;
- // Our implementation in Detail must be equivalent to Std (if Std supports
- // __int128).
+ // Our implementation in Detail must be equivalent to Std
+ // (if Std supports __int128).
if constexpr (Std::is_specialized) {
TEST(Detail::is_specialized == Std::is_specialized);
TEST(Detail::is_signed == Std::is_signed);
>From c4a8ff7d1a0c3cddc3c358d5d957f612745d17f6 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 6 Oct 2026 17:22:20 +0200
Subject: [PATCH 09/15] Add header numeric-limits.h
---
.../ExternalHelloWorld/external-hello.cpp | 6 +-
.../flang-rt/runtime/format-implementation.h | 4 +-
.../flang-rt/runtime/numeric-templates.h | 4 +-
flang-rt/lib/quadmath/scale.cpp | 4 +-
flang-rt/lib/runtime/command.cpp | 2 +-
flang-rt/lib/runtime/environment.cpp | 4 +-
flang-rt/lib/runtime/extensions.cpp | 4 +-
flang-rt/lib/runtime/external-unit.cpp | 2 +-
flang-rt/lib/runtime/io-stmt.cpp | 5 +-
flang-rt/lib/runtime/numeric.cpp | 4 +-
flang-rt/lib/runtime/unit.cpp | 2 +-
flang-rt/unittests/Runtime/Complex.cpp | 8 +-
flang-rt/unittests/Runtime/Namelist.cpp | 10 +-
flang-rt/unittests/Runtime/Reduction.cpp | 19 ++--
flang/include/flang/Common/erfc-scaled.h | 6 +-
flang/include/flang/Common/format.h | 10 +-
flang/include/flang/Common/numeric-limits.h | 106 ++++++++++++++++++
flang/include/flang/Common/uint128.h | 74 +-----------
flang/include/flang/Evaluate/integer.h | 13 ++-
.../include/flang/Optimizer/Dialect/FIROps.td | 2 +-
flang/lib/Decimal/big-radix-floating-point.h | 2 +-
flang/lib/Evaluate/fold-implementation.h | 2 +-
flang/lib/Evaluate/host.h | 12 +-
flang/lib/Lower/Bridge.cpp | 2 +-
flang/lib/Lower/CUDA.cpp | 4 +-
flang/lib/Lower/ConvertConstant.cpp | 2 +-
flang/lib/Lower/PFTBuilder.cpp | 2 +-
flang/lib/Optimizer/CodeGen/TBAABuilder.cpp | 2 +-
flang/lib/Optimizer/Dialect/FIRType.cpp | 2 +-
flang/lib/Parser/token-parsers.h | 10 +-
flang/lib/Semantics/compute-offsets.cpp | 2 +-
flang/lib/Semantics/expression.cpp | 2 +-
flang/lib/Semantics/openmp-utils.cpp | 10 +-
flang/unittests/Evaluate/uint128.cpp | 23 ++--
34 files changed, 202 insertions(+), 164 deletions(-)
create mode 100644 flang/include/flang/Common/numeric-limits.h
diff --git a/flang-rt/examples/ExternalHelloWorld/external-hello.cpp b/flang-rt/examples/ExternalHelloWorld/external-hello.cpp
index 7c8a12476295ce..37288a3e086028 100644
--- a/flang-rt/examples/ExternalHelloWorld/external-hello.cpp
+++ b/flang-rt/examples/ExternalHelloWorld/external-hello.cpp
@@ -22,9 +22,9 @@ void output1() {
IONAME(OutputReal64)(io, 0.0);
IONAME(OutputReal64)(io, 2.0 / 3.0);
IONAME(OutputReal64)(io, 1.0e99);
- IONAME(OutputReal64)(io, std::numeric_limits<double>::infinity());
- IONAME(OutputReal64)(io, -std::numeric_limits<double>::infinity());
- IONAME(OutputReal64)(io, std::numeric_limits<double>::quiet_NaN());
+ IONAME(OutputReal64)(io, common::numeric_limits<double>::infinity());
+ IONAME(OutputReal64)(io, -common::numeric_limits<double>::infinity());
+ IONAME(OutputReal64)(io, common::numeric_limits<double>::quiet_NaN());
IONAME(OutputComplex64)(io, 123.0, -234.0);
IONAME(OutputLogical)(io, false);
IONAME(OutputLogical)(io, true);
diff --git a/flang-rt/include/flang-rt/runtime/format-implementation.h b/flang-rt/include/flang-rt/runtime/format-implementation.h
index 377298545fc2a7..6a0a1b12003e60 100644
--- a/flang-rt/include/flang-rt/runtime/format-implementation.h
+++ b/flang-rt/include/flang-rt/runtime/format-implementation.h
@@ -85,9 +85,9 @@ RT_API_ATTRS int FormatControl<CONTEXT>::GetIntField(
}
int result{0};
while (ch >= '0' && ch <= '9') {
- constexpr int tenth{std::numeric_limits<int>::max() / 10};
+ constexpr int tenth{common::numeric_limits<int>::max() / 10};
if (result > tenth ||
- ch - '0' > std::numeric_limits<int>::max() - 10 * result) {
+ ch - '0' > common::numeric_limits<int>::max() - 10 * result) {
handler.SignalError(
IostatErrorInFormat, "FORMAT integer field out of range");
if (hadError) {
diff --git a/flang-rt/include/flang-rt/runtime/numeric-templates.h b/flang-rt/include/flang-rt/runtime/numeric-templates.h
index 7733a32c98f590..e8def3f772a240 100644
--- a/flang-rt/include/flang-rt/runtime/numeric-templates.h
+++ b/flang-rt/include/flang-rt/runtime/numeric-templates.h
@@ -242,8 +242,8 @@ inline RT_API_ATTRS T RealMod(
}
T aAbs{ABSTy<T>::compute(a)};
T pAbs{ABSTy<T>::compute(p)};
- if (aAbs <= static_cast<T>(std::numeric_limits<std::int64_t>::max()) &&
- pAbs <= static_cast<T>(std::numeric_limits<std::int64_t>::max())) {
+ if (aAbs <= static_cast<T>(common::numeric_limits<std::int64_t>::max()) &&
+ pAbs <= static_cast<T>(common::numeric_limits<std::int64_t>::max())) {
if (auto aInt{static_cast<std::int64_t>(a)}; a == aInt) {
if (auto pInt{static_cast<std::int64_t>(p)}; p == pInt) {
// Fast exact case for integer operands
diff --git a/flang-rt/lib/quadmath/scale.cpp b/flang-rt/lib/quadmath/scale.cpp
index 3d919f85a44877..5498d3b177591c 100644
--- a/flang-rt/lib/quadmath/scale.cpp
+++ b/flang-rt/lib/quadmath/scale.cpp
@@ -17,8 +17,8 @@ extern "C" {
F128Type RTDEF(Scale16)(F128Type x, std::int64_t p) {
auto ip{static_cast<int>(p)};
if (ip != p) {
- ip = p < 0 ? std::numeric_limits<int>::min()
- : std::numeric_limits<int>::max();
+ ip = p < 0 ? common::numeric_limits<int>::min()
+ : common::numeric_limits<int>::max();
}
return LDEXPTy<F128Type>::compute(x, ip);
}
diff --git a/flang-rt/lib/runtime/command.cpp b/flang-rt/lib/runtime/command.cpp
index 66e1fc787dfa0f..6c2089ccc33b0f 100644
--- a/flang-rt/lib/runtime/command.cpp
+++ b/flang-rt/lib/runtime/command.cpp
@@ -49,7 +49,7 @@ pid_t RTNAME(GetPID)() { return getpid(); }
// Returns the length of the \p string. Assumes \p string is valid.
static std::int64_t StringLength(const char *string) {
std::size_t length{std::strlen(string)};
- if (length <= std::numeric_limits<std::int64_t>::max())
+ if (length <= common::numeric_limits<std::int64_t>::max())
return static_cast<std::int64_t>(length);
return 0;
}
diff --git a/flang-rt/lib/runtime/environment.cpp b/flang-rt/lib/runtime/environment.cpp
index 0203d48443a3b1..202b24be624a1a 100644
--- a/flang-rt/lib/runtime/environment.cpp
+++ b/flang-rt/lib/runtime/environment.cpp
@@ -289,7 +289,7 @@ void ExecutionEnvironment::Configure(int ac, const char *av[],
if (auto *x{std::getenv("FORT_FMT_RECL")}) {
char *end;
auto n{std::strtol(x, &end, 10)};
- if (n > 0 && n < std::numeric_limits<int>::max() && *end == '\0') {
+ if (n > 0 && n < common::numeric_limits<int>::max() && *end == '\0') {
listDirectedOutputLineLengthLimit = n;
} else {
std::fprintf(
@@ -392,7 +392,7 @@ void ExecutionEnvironment::Configure(int ac, const char *av[],
if (auto *x{std::getenv("ACC_OFFLOAD_STACK_SIZE")}) {
char *end;
auto n{std::strtoul(x, &end, 10)};
- if (n > 0 && n != std::numeric_limits<unsigned long>::max() &&
+ if (n > 0 && n != common::numeric_limits<unsigned long>::max() &&
*end == '\0') {
cudaStackLimit = n;
} else {
diff --git a/flang-rt/lib/runtime/extensions.cpp b/flang-rt/lib/runtime/extensions.cpp
index b159c7747726f7..340ad07c925dce 100644
--- a/flang-rt/lib/runtime/extensions.cpp
+++ b/flang-rt/lib/runtime/extensions.cpp
@@ -505,8 +505,8 @@ int RTNAME(Irand)(int *i) {
// RAND(I)
float RTNAME(Rand)(int *i, const char *sourceFile, int line) {
unsigned mask = 0;
- constexpr int radix = std::numeric_limits<float>::radix;
- constexpr int digits = std::numeric_limits<float>::digits;
+ constexpr int radix = common::numeric_limits<float>::radix;
+ constexpr int digits = common::numeric_limits<float>::digits;
if constexpr (radix == 2) {
mask = ~(unsigned)0u << (32 - digits + 1);
} else {
diff --git a/flang-rt/lib/runtime/external-unit.cpp b/flang-rt/lib/runtime/external-unit.cpp
index 0f7ab286687462..0c232c18711155 100644
--- a/flang-rt/lib/runtime/external-unit.cpp
+++ b/flang-rt/lib/runtime/external-unit.cpp
@@ -203,7 +203,7 @@ bool ExternalFileUnit::OpenUnit(common::optional<OpenStatus> status,
if (access != Access::Stream) {
if (!endfileRecordNumber) {
// Fake it so that we can backspace relative from the end
- endfileRecordNumber = std::numeric_limits<std::int64_t>::max() - 2;
+ endfileRecordNumber = common::numeric_limits<std::int64_t>::max() - 2;
}
currentRecordNumber = *endfileRecordNumber;
}
diff --git a/flang-rt/lib/runtime/io-stmt.cpp b/flang-rt/lib/runtime/io-stmt.cpp
index 05032a1e9da341..4034254df14d52 100644
--- a/flang-rt/lib/runtime/io-stmt.cpp
+++ b/flang-rt/lib/runtime/io-stmt.cpp
@@ -923,7 +923,8 @@ ListDirectedStatementState<Direction::Input>::GetNextDataEdit(
auto start{fastField.connection().positionInRecord};
int r{0};
do {
- static auto constexpr clamp{(std::numeric_limits<int>::max() - '9') / 10};
+ static auto constexpr clamp{
+ (common::numeric_limits<int>::max() - '9') / 10};
if (r >= clamp) {
r = 0;
break;
@@ -1485,7 +1486,7 @@ bool InquireUnitState::Inquire(
} else if (unit().openRecl) {
result = *unit().openRecl;
} else {
- result = std::numeric_limits<std::int32_t>::max();
+ result = common::numeric_limits<std::int32_t>::max();
}
return true;
case HashInquiryKeyword("SIZE"):
diff --git a/flang-rt/lib/runtime/numeric.cpp b/flang-rt/lib/runtime/numeric.cpp
index 835897680d6f1f..a5191fbbe1f9fb 100644
--- a/flang-rt/lib/runtime/numeric.cpp
+++ b/flang-rt/lib/runtime/numeric.cpp
@@ -88,8 +88,8 @@ inline RT_API_ATTRS T IntMod(T x, T p, const char *sourceFile, int sourceLine) {
template <typename T> inline RT_API_ATTRS T Scale(T x, std::int64_t p) {
auto ip{static_cast<int>(p)};
if (ip != p) {
- ip = p < 0 ? std::numeric_limits<int>::min()
- : std::numeric_limits<int>::max();
+ ip = p < 0 ? common::numeric_limits<int>::min()
+ : common::numeric_limits<int>::max();
}
return std::ldexp(x, ip); // x*2**p
}
diff --git a/flang-rt/lib/runtime/unit.cpp b/flang-rt/lib/runtime/unit.cpp
index cbf802d3b8225c..58f40b36f4ba2a 100644
--- a/flang-rt/lib/runtime/unit.cpp
+++ b/flang-rt/lib/runtime/unit.cpp
@@ -531,7 +531,7 @@ void ExternalFileUnit::Sought(std::int64_t zeroBasedPos) {
} else {
// We no longer know which record we're in. Set currentRecordNumber to
// a large value from whence we can both advance and backspace.
- currentRecordNumber = std::numeric_limits<std::int64_t>::max() / 2;
+ currentRecordNumber = common::numeric_limits<std::int64_t>::max() / 2;
endfileRecordNumber.reset();
}
}
diff --git a/flang-rt/unittests/Runtime/Complex.cpp b/flang-rt/unittests/Runtime/Complex.cpp
index d7e5f55414fe2b..03b10021a279b4 100644
--- a/flang-rt/unittests/Runtime/Complex.cpp
+++ b/flang-rt/unittests/Runtime/Complex.cpp
@@ -113,7 +113,7 @@ TEST(Complex, cpowi) {
EXPECT_COMPLEX_FLOAT_EQ(cpowi(0.5f + 0.6if, -5), -1.121837f + 3.252915if);
EXPECT_COMPLEX_FLOAT_EQ(
- cpowi(0.f + 1if, std::numeric_limits<std::int32_t>::min()), 1.f + 0if);
+ cpowi(0.f + 1if, common::numeric_limits<std::int32_t>::min()), 1.f + 0if);
}
TEST(Complex, cpowk) {
@@ -131,7 +131,7 @@ TEST(Complex, cpowk) {
EXPECT_COMPLEX_FLOAT_EQ(cpowk(0.5f + 0.6if, -5), -1.121837f + 3.252915if);
EXPECT_COMPLEX_FLOAT_EQ(
- cpowk(0.f + 1if, std::numeric_limits<std::int64_t>::min()), 1.f + 0if);
+ cpowk(0.f + 1if, common::numeric_limits<std::int64_t>::min()), 1.f + 0if);
}
TEST(Complex, zpowi) {
@@ -149,7 +149,7 @@ TEST(Complex, zpowi) {
EXPECT_COMPLEX_DOUBLE_EQ(zpowi(0.5 + 0.6i, -5), -1.12183773 + 3.25291503i);
EXPECT_COMPLEX_DOUBLE_EQ(
- zpowi(0. + 1i, std::numeric_limits<std::int32_t>::min()), 1. + 0i);
+ zpowi(0. + 1i, common::numeric_limits<std::int32_t>::min()), 1. + 0i);
}
TEST(Complex, zpowk) {
@@ -167,5 +167,5 @@ TEST(Complex, zpowk) {
EXPECT_COMPLEX_DOUBLE_EQ(zpowk(0.5 + 0.6i, -5), -1.12183773 + 3.25291503i);
EXPECT_COMPLEX_DOUBLE_EQ(
- zpowk(0. + 1i, std::numeric_limits<std::int64_t>::min()), 1. + 0i);
+ zpowk(0. + 1i, common::numeric_limits<std::int64_t>::min()), 1. + 0i);
}
diff --git a/flang-rt/unittests/Runtime/Namelist.cpp b/flang-rt/unittests/Runtime/Namelist.cpp
index 5db7c0b4774370..992c1357b9e754 100644
--- a/flang-rt/unittests/Runtime/Namelist.cpp
+++ b/flang-rt/unittests/Runtime/Namelist.cpp
@@ -22,6 +22,7 @@
using namespace Fortran::runtime;
using namespace Fortran::runtime::io;
+using Fortran::common::numeric_limits;
struct NamelistTests : CrashHandlerFixture {};
@@ -44,11 +45,10 @@ TEST(NamelistTests, BasicSanity) {
for (int j{0}; j < 20; ++j) {
ints.push_back(j % 2 == 0 ? (1 << j) : -(1 << j));
}
- std::vector<double> reals{0.0, -0.0, std::numeric_limits<double>::infinity(),
- -std::numeric_limits<double>::infinity(),
- std::numeric_limits<double>::quiet_NaN(),
- std::numeric_limits<double>::max(), std::numeric_limits<double>::lowest(),
- std::numeric_limits<double>::epsilon()};
+ std::vector<double> reals{0.0, -0.0, numeric_limits<double>::infinity(),
+ -numeric_limits<double>::infinity(), numeric_limits<double>::quiet_NaN(),
+ numeric_limits<double>::max(), numeric_limits<double>::lowest(),
+ numeric_limits<double>::epsilon()};
std::vector<std::uint8_t> logicals;
logicals.push_back(false);
logicals.push_back(true);
diff --git a/flang-rt/unittests/Runtime/Reduction.cpp b/flang-rt/unittests/Runtime/Reduction.cpp
index 50824cf19146e5..9ed42cb8c6a7b8 100644
--- a/flang-rt/unittests/Runtime/Reduction.cpp
+++ b/flang-rt/unittests/Runtime/Reduction.cpp
@@ -23,6 +23,7 @@
#include <vector>
using namespace Fortran::runtime;
+using Fortran::numeric_limits;
using Fortran::common::TypeCategory;
TEST(Reductions, Int4Ops) {
@@ -481,8 +482,8 @@ TEST(Reductions, FindlocNumeric) {
std::vector<int> shape{2, 3};
auto realArray{MakeArray<TypeCategory::Real, 8>(shape,
std::vector<double>{0.0, -0.0, 1.0, 3.14,
- std::numeric_limits<double>::quiet_NaN(),
- std::numeric_limits<double>::infinity()})};
+ numeric_limits<double>::quiet_NaN(),
+ numeric_limits<double>::infinity()})};
ASSERT_EQ(realArray->ElementBytes(), sizeof(double));
StaticDescriptor<2, true> statDesc[2];
Descriptor &res{statDesc[0].descriptor()};
@@ -511,7 +512,7 @@ TEST(Reductions, FindlocNumeric) {
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 1);
res.Destroy();
// Find the +Inf
- value = std::numeric_limits<double>::infinity();
+ value = numeric_limits<double>::infinity();
RTNAME(Findloc)
(res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
@@ -522,7 +523,7 @@ TEST(Reductions, FindlocNumeric) {
EXPECT_EQ(*res.ZeroBasedIndexedElement<SubscriptValue>(1), 3);
res.Destroy();
// Ensure that we can't find a NaN
- value = std::numeric_limits<double>::quiet_NaN();
+ value = numeric_limits<double>::quiet_NaN();
RTNAME(Findloc)
(res, *realArray, target, 8, __FILE__, __LINE__, nullptr, /*BACK=*/false);
EXPECT_EQ(res.rank(), 1);
@@ -575,8 +576,8 @@ TEST(Reductions, FindlocNumeric) {
std::vector<int> shape1{6};
auto realArray1{MakeArray<TypeCategory::Real, 8>(shape1,
std::vector<double>{0.0, -0.0, 1.0, 3.14,
- std::numeric_limits<double>::quiet_NaN(),
- std::numeric_limits<double>::infinity()})};
+ numeric_limits<double>::quiet_NaN(),
+ numeric_limits<double>::infinity()})};
StaticDescriptor<1, true> statDesc0[1];
Descriptor &scalarResult{statDesc0[0].descriptor()};
RTNAME(FindlocDim)
@@ -710,7 +711,7 @@ TEST(Reductions, ReduceInt4Dim) {
}
TEST(Reductions, InfSums) {
- auto inf{std::numeric_limits<float>::infinity()};
+ auto inf{numeric_limits<float>::infinity()};
auto inf0{MakeArray<TypeCategory::Real, 4>(
std::vector<int>{2}, std::vector<float>{inf, 0.0f})};
auto t1{RTNAME(SumReal4)(*inf0, __FILE__, __LINE__)};
@@ -757,12 +758,12 @@ TEST(Reductions, RealProductNoEarlyExit) {
EXPECT_FALSE(std::signbit(r8));
// A NaN after a zero must propagate.
auto zeroThenNaN{MakeArray<TypeCategory::Real, 4>(std::vector<int>{2},
- std::vector<float>{0.0f, std::numeric_limits<float>::quiet_NaN()})};
+ std::vector<float>{0.0f, numeric_limits<float>::quiet_NaN()})};
float r4{RTNAME(ProductReal4)(*zeroThenNaN, __FILE__, __LINE__)};
EXPECT_NE(r4, r4) << r4;
// No zero element: the running product underflows to +0 before the
// negative factor is reached, so the result is -0.
- auto tiny{std::numeric_limits<double>::min()};
+ auto tiny{numeric_limits<double>::min()};
auto underflow{MakeArray<TypeCategory::Real, 8>(
std::vector<int>{3}, std::vector<double>{tiny, tiny, -2.0})};
double u8{RTNAME(ProductReal8)(*underflow, __FILE__, __LINE__)};
diff --git a/flang/include/flang/Common/erfc-scaled.h b/flang/include/flang/Common/erfc-scaled.h
index 001af66baf34a4..9be61de162f180 100644
--- a/flang/include/flang/Common/erfc-scaled.h
+++ b/flang/include/flang/Common/erfc-scaled.h
@@ -39,7 +39,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
constexpr T sqrtpi{1.7724538509078120380404576221783883301349L};
constexpr T rsqrtpi{0.5641895835477562869480794515607725858440L};
- constexpr T epsilonby2{std::numeric_limits<T>::epsilon() * 0.5};
+ constexpr T epsilonby2{common::numeric_limits<T>::epsilon() * 0.5};
constexpr T xneg{-26.628e0};
constexpr T xhuge{6.71e7};
constexpr T thresh{0.46875e0};
@@ -47,7 +47,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
constexpr T one{1.0};
constexpr T four{4.0};
constexpr T sixteen{16.0};
- constexpr T xmax{1.0 / (sqrtpi * std::numeric_limits<T>::min())};
+ constexpr T xmax{1.0 / (sqrtpi * common::numeric_limits<T>::min())};
static_assert(xmax > xhuge, "xmax must be greater than xhuge");
T ysq;
@@ -106,7 +106,7 @@ template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
// fix up for negative argument, erf, etc.
if (x < zero) {
if (x < xneg) {
- result = std::numeric_limits<T>::max();
+ result = common::numeric_limits<T>::max();
} else {
ysq = trunc(x * sixteen) / sixteen;
del = (x - ysq) * (x + ysq);
diff --git a/flang/include/flang/Common/format.h b/flang/include/flang/Common/format.h
index 8ecb0682536dd9..0a22f867ce9610 100644
--- a/flang/include/flang/Common/format.h
+++ b/flang/include/flang/Common/format.h
@@ -91,13 +91,13 @@ static inline bool MulOverflow(
// Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
// positive) divided by an argument compares to the other.
if (isNegative) {
- return ux >
- (static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()) +
- std::uint64_t{1}) /
+ return ux > (static_cast<std::uint64_t>(
+ common::numeric_limits<std::int64_t>::max()) +
+ std::uint64_t{1}) /
uy;
} else {
- return ux >
- (static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) /
+ return ux > (static_cast<std::uint64_t>(
+ common::numeric_limits<std::int64_t>::max())) /
uy;
}
#endif
diff --git a/flang/include/flang/Common/numeric-limits.h b/flang/include/flang/Common/numeric-limits.h
new file mode 100644
index 00000000000000..7e64a192f58fdf
--- /dev/null
+++ b/flang/include/flang/Common/numeric-limits.h
@@ -0,0 +1,106 @@
+//===-- include/flang/Common/numeric-limits.h -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_COMMON_NUMERIC_LIMITS_H_
+#define FORTRAN_COMMON_NUMERIC_LIMITS_H_
+
+#include <limits>
+#include <type_traits>
+
+namespace Fortran::common {
+
+namespace detail {
+
+template <typename T> class numeric_limits_impl {
+public:
+ static constexpr bool is_specialized{false};
+};
+
+#if defined(__SIZEOF_INT128__)
+// Handle discrepancy of support of bit 128 bit integers by compiler and
+// standard library. The compiler may treat __int128 as a builtin type, but the
+// standard library does not define common::numeric_limits for it. Two cases are
+// known:
+//
+// 1. clang-cl supports __int128, but MSVC, and therefore its STL used by
+// clang-cl, does not.
+//
+// 2. Some versions of libstdc++ in strict mode (-std=c++NN)
+// intentionally remove any use of __int128, even though gcc does not make
+// such a distinction.
+//
+// Using __int128_t/__uint128_t typedefs; spelling out the __int128
+// keyword is a warning "ISO C++ does not support ‘__int128’ for ‘type name’"
+// under -Wpedantic
+
+template <> class numeric_limits_impl<__uint128_t> {
+public:
+ using T = __uint128_t;
+
+ static constexpr bool is_specialized{true};
+ static constexpr bool is_signed{false};
+ static constexpr bool is_integer{true};
+
+ static constexpr T min() { return static_cast<T>(0); }
+ static constexpr T max() { return ~static_cast<T>(0); }
+ static constexpr T lowest() { return min(); }
+};
+
+template <> class numeric_limits_impl<__int128_t> {
+public:
+ using T = __int128_t;
+
+ static constexpr bool is_specialized{true};
+ static constexpr bool is_signed{true};
+ static constexpr bool is_integer{true};
+
+ static constexpr T min() {
+ return static_cast<T>(static_cast<__uint128_t>(1) << 127u);
+ }
+ static constexpr T max() {
+ return static_cast<T>(~(static_cast<__uint128_t>(1) << 127u));
+ }
+ static constexpr T lowest() { return min(); }
+};
+#endif
+
+// TODO: Workaround for __float128 needed as well
+
+template <typename T>
+using numeric_limits = numeric_limits_impl<std::remove_cv_t<T>>;
+
+} // namespace detail
+
+/// Same as std::numeric_limits, but also defined for 128-bit integers. While
+/// std::numeric_limits is allowed to be extended for user-defined types such
+/// as UnsignedInt128/SignedInt128 (C++ [namespace.std]), it is not for
+/// non-standard compiler extentions such as
+/// __int128/__uint128/__float128.
+///
+/// Only defining members that are actually used in Flang/Flang-RT. Feel free to
+/// add more members as needed.
+template <typename T>
+using numeric_limits =
+ typename std::conditional_t<detail::numeric_limits<T>::is_specialized &&
+ !std::numeric_limits<T>::is_specialized,
+ detail::numeric_limits<T>, std::numeric_limits<T>>;
+
+template <typename T>
+static inline constexpr bool is_arithmetic_v{numeric_limits<T>::is_specialized};
+template <typename T>
+static inline constexpr bool is_integral_v{
+ numeric_limits<T>::is_specialized && numeric_limits<T>::is_integer};
+template <typename T>
+static inline constexpr bool is_signed_v{
+ numeric_limits<T>::is_specialized && numeric_limits<T>::is_signed};
+template <typename T>
+static inline constexpr bool is_unsigned_v{
+ numeric_limits<T>::is_specialized && !numeric_limits<T>::is_signed};
+
+} // namespace Fortran::common
+#endif // FORTRAN_COMMON_NUMERIC_LIMITS_H_
diff --git a/flang/include/flang/Common/uint128.h b/flang/include/flang/Common/uint128.h
index f53cb81778c1a6..e3bfb5d63e4f6e 100644
--- a/flang/include/flang/Common/uint128.h
+++ b/flang/include/flang/Common/uint128.h
@@ -21,14 +21,11 @@
#include "api-attrs.h"
#include "leading-zero-bit-count.h"
+#include "numeric-limits.h"
#include <cstdint>
-#include <limits>
#include <type_traits>
namespace Fortran::common {
-namespace detail {
-template <typename T> class numeric_limits_impl;
-}
template <bool IS_SIGNED = false> class Int128 {
friend class detail::numeric_limits_impl<Int128>;
@@ -312,11 +309,6 @@ using HostSignedIntType = typename HostSignedIntTypeHelper<BITS>::type;
namespace detail {
-template <typename T> class numeric_limits_impl {
-public:
- static constexpr bool is_specialized{false};
-};
-
template <> class numeric_limits_impl<Fortran::common::UnsignedInt128> {
public:
using T = Fortran::common::UnsignedInt128;
@@ -347,71 +339,7 @@ template <> class numeric_limits_impl<Fortran::common::SignedInt128> {
static constexpr T lowest() { return min(); }
};
-#if defined(__SIZEOF_INT128__)
-// Handle discrepancy of support of bit 128 bit integers by compiler and
-// standard library. The compiler may treat __int128 as a builtin type, but the
-// standard library does not define std::numeric_limits for it. Two cases are
-// known:
-//
-// 1. clang-cl supports __int128, but MSVC, and therefore its STL used by
-// clang-cl, does not.
-//
-// 2. Some versions of libstdc++ in strict mode (-std=c++NN)
-// intentionally removes any use of __int128, even though gcc does not make
-// such a distinction.
-//
-// Using __int128_t/__uint128_t typedefs; spelling out the __int128
-// keyword is a warning "ISO C++ does not support ‘__int128’ for ‘type name’"
-// under -Wpedantic
-
-template <> class numeric_limits_impl<__uint128_t> {
-public:
- using T = __uint128_t;
-
- static constexpr bool is_specialized{true};
- static constexpr bool is_signed{false};
- static constexpr bool is_integer{true};
-
- static constexpr T min() { return static_cast<T>(0); }
- static constexpr T max() { return ~static_cast<T>(0); }
- static constexpr T lowest() { return min(); }
-};
-
-template <> class numeric_limits_impl<__int128_t> {
-public:
- using T = __int128_t;
-
- static constexpr bool is_specialized{true};
- static constexpr bool is_signed{true};
- static constexpr bool is_integer{true};
-
- static constexpr T min() {
- return static_cast<T>(static_cast<__uint128_t>(1) << 127u);
- }
- static constexpr T max() {
- return static_cast<T>(~(static_cast<__uint128_t>(1) << 127u));
- }
- static constexpr T lowest() { return min(); }
-};
-#endif
-
-template <typename T>
-using numeric_limits = numeric_limits_impl<std::remove_cv_t<T>>;
-
} // namespace detail
-/// Same as std::numeric_limits, but also defined for 128 bit integers. While
-/// std::numeric_limits is allowed to be extended for user-defined types such as
-/// UnsignedInt128/SignedInt128 (C++ [namespace.std]), it is not for the
-/// __int128/__uint128 workaround above.
-///
-/// Only defining the members actually used in Flang/Flang-RT. Feel free to add
-/// more members as needed.
-template <typename T>
-using numeric_limits =
- std::conditional_t<detail::numeric_limits<T>::is_specialized &&
- !std::numeric_limits<T>::is_specialized,
- detail::numeric_limits<T>, std::numeric_limits<T>>;
-
} // namespace Fortran::common
#endif
diff --git a/flang/include/flang/Evaluate/integer.h b/flang/include/flang/Evaluate/integer.h
index fade8cbcc114f2..3ab68945f1cfb7 100644
--- a/flang/include/flang/Evaluate/integer.h
+++ b/flang/include/flang/Evaluate/integer.h
@@ -68,10 +68,10 @@ class Integer {
static constexpr int partBits{PARTBITS};
using Part = PART;
using BigPart = BIGPART;
- static_assert(std::is_integral_v<Part>);
- static_assert(std::is_unsigned_v<Part>);
- static_assert(std::is_integral_v<BigPart>);
- static_assert(std::is_unsigned_v<BigPart>);
+ static_assert(common::is_integral_v<Part>);
+ static_assert(common::is_unsigned_v<Part>);
+ static_assert(common::is_integral_v<BigPart>);
+ static_assert(common::is_unsigned_v<BigPart>);
static_assert(CHAR_BIT * sizeof(BigPart) >= 2 * partBits);
static constexpr bool littleEndian{IS_LITTLE_ENDIAN};
static constexpr int alignment{ALIGNMENT};
@@ -128,11 +128,12 @@ class Integer {
// C++'s integral types can all be converted to Integer
// with silent truncation.
- template <typename INT, typename = std::enable_if_t<std::is_integral_v<INT>>>
+ template <typename INT,
+ typename = std::enable_if_t<common::is_integral_v<INT>>>
constexpr Integer(INT n) {
constexpr int nBits = CHAR_BIT * sizeof n;
if constexpr (nBits < partBits) {
- if constexpr (std::is_unsigned_v<INT>) {
+ if constexpr (common::is_unsigned_v<INT>) {
// Zero-extend an unsigned smaller value.
SetLEPart(0, n);
for (int j{1}; j < parts; ++j) {
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 7fc12430d0663f..6dadf2eb7b4888 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -1660,7 +1660,7 @@ def fir_CoordinateOp
"mlir::Value":$ref, "llvm::ArrayRef<fir::IntOrValue>":$coor)>
];
let extraClassDeclaration = [{
- constexpr static int32_t kDynamicIndex = std::numeric_limits<int32_t>::min();
+ constexpr static int32_t kDynamicIndex = common::numeric_limits<int32_t>::min();
CoordinateIndicesAdaptor getIndices();
// FortranObjectViewOpInterface methods:
diff --git a/flang/lib/Decimal/big-radix-floating-point.h b/flang/lib/Decimal/big-radix-floating-point.h
index f775b6939df31d..ebc89a2d9c5328 100644
--- a/flang/lib/Decimal/big-radix-floating-point.h
+++ b/flang/lib/Decimal/big-radix-floating-point.h
@@ -149,7 +149,7 @@ template <int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
// Sets *this to an unsigned integer value.
// Returns any remainder.
template <typename UINT> RT_API_ATTRS UINT SetTo(UINT n) {
- static_assert(!common::numeric_limits<UINT>::is_signed);
+ static_assert(common::is_unsigned_v<UINT>);
SetToZero();
while (n != 0) {
auto q{n / 10u};
diff --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index e775efa4450579..4fe69577323ae6 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -581,7 +581,7 @@ std::optional<std::int64_t> GetInt64ArgOr(
template <typename A, typename B>
std::optional<std::vector<A>> GetIntegerVector(const B &x) {
- static_assert(common::numeric_limits<A>::is_integer);
+ static_assert(common::is_integral_v<A>);
if (const auto *someInteger{UnwrapExpr<Expr<SomeInteger>>(x)}) {
return common::visit(
[](const auto &typedExpr) -> std::optional<std::vector<A>> {
diff --git a/flang/lib/Evaluate/host.h b/flang/lib/Evaluate/host.h
index e032b28ab8a3d7..3cab995a8824a7 100644
--- a/flang/lib/Evaluate/host.h
+++ b/flang/lib/Evaluate/host.h
@@ -133,7 +133,7 @@ struct HostTypeHelper<
Type<TypeCategory::Real, common::RealKindForPrecision(24)>> {
// IEEE 754 32bits
using Type = std::conditional_t<sizeof(float) == 4 &&
- std::numeric_limits<float>::is_iec559,
+ common::numeric_limits<float>::is_iec559,
float, UnsupportedType>;
};
@@ -142,7 +142,7 @@ struct HostTypeHelper<
Type<TypeCategory::Real, common::RealKindForPrecision(53)>> {
// IEEE 754 64bits
using Type = std::conditional_t<sizeof(double) == 8 &&
- std::numeric_limits<double>::is_iec559,
+ common::numeric_limits<double>::is_iec559,
double, UnsupportedType>;
};
@@ -151,8 +151,8 @@ struct HostTypeHelper<
Type<TypeCategory::Real, common::RealKindForPrecision(64)>> {
// X87 80bits
using Type = std::conditional_t<sizeof(long double) >= 10 &&
- std::numeric_limits<long double>::digits == 64 &&
- std::numeric_limits<long double>::max_exponent == 16384,
+ common::numeric_limits<long double>::digits == 64 &&
+ common::numeric_limits<long double>::max_exponent == 16384,
long double, UnsupportedType>;
};
@@ -165,8 +165,8 @@ template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
template <> struct HostTypeHelper<Type<TypeCategory::Real, 16>> {
// IEEE 754 128bits
using Type = std::conditional_t<sizeof(long double) == 16 &&
- std::numeric_limits<long double>::digits == 113 &&
- std::numeric_limits<long double>::max_exponent == 16384,
+ common::numeric_limits<long double>::digits == 113 &&
+ common::numeric_limits<long double>::max_exponent == 16384,
long double, UnsupportedType>;
};
#endif
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 939d8d134552e9..792b4851e560aa 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -4895,7 +4895,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
llvm::SmallVector<mlir::Attribute> attrList;
llvm::SmallVector<mlir::Block *> blockList;
unsigned typeGuardIdx = 0;
- std::size_t defaultAttrPos = std::numeric_limits<size_t>::max();
+ std::size_t defaultAttrPos = common::numeric_limits<size_t>::max();
bool hasLocalScope = false;
llvm::SmallVector<const Fortran::semantics::Scope *> typeCaseScopes;
diff --git a/flang/lib/Lower/CUDA.cpp b/flang/lib/Lower/CUDA.cpp
index d8e2d829f9adfc..adb3e3e6b604ab 100644
--- a/flang/lib/Lower/CUDA.cpp
+++ b/flang/lib/Lower/CUDA.cpp
@@ -23,7 +23,7 @@ mlir::Type Fortran::lower::gatherDeviceComponentCoordinatesAndType(
llvm::SmallVector<mlir::Value> &coordinates) {
unsigned fieldIdx = recTy.getFieldIndex(sym.name().ToString());
mlir::Type fieldTy;
- if (fieldIdx != std::numeric_limits<unsigned>::max()) {
+ if (fieldIdx != common::numeric_limits<unsigned>::max()) {
// Field found in the base record type.
auto fieldName = recTy.getTypeList()[fieldIdx].first;
fieldTy = recTy.getTypeList()[fieldIdx].second;
@@ -38,7 +38,7 @@ mlir::Type Fortran::lower::gatherDeviceComponentCoordinatesAndType(
for (auto component : recTy.getTypeList()) {
if (auto childRecTy = mlir::dyn_cast<fir::RecordType>(component.second)) {
fieldIdx = childRecTy.getFieldIndex(sym.name().ToString());
- if (fieldIdx != std::numeric_limits<unsigned>::max()) {
+ if (fieldIdx != common::numeric_limits<unsigned>::max()) {
mlir::Value parentFieldIndex = fir::FieldIndexOp::create(
builder, loc, fir::FieldType::get(childRecTy.getContext()),
component.first, recTy,
diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp
index f48dc9931e627e..9260db58d1a868 100644
--- a/flang/lib/Lower/ConvertConstant.cpp
+++ b/flang/lib/Lower/ConvertConstant.cpp
@@ -753,7 +753,7 @@ genArrayLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
Fortran::evaluate::ConstantSubscript size =
Fortran::evaluate::GetSize(con.shape());
- if (size > std::numeric_limits<std::uint32_t>::max())
+ if (size > common::numeric_limits<std::uint32_t>::max())
// llvm::SmallVector has limited size
TODO(loc, "Creation of very large array constants");
fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 536ba708b19161..1ecce4b82d2a46 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -2650,7 +2650,7 @@ constantValueOrMax(const parser::ScalarIntConstantExpr &intExpr) {
if (auto v = evaluate::ToInt64(*expr))
if (*v > 0)
return *v;
- return std::numeric_limits<int64_t>::max();
+ return common::numeric_limits<int64_t>::max();
}
/// Fill \p chain with the DoConstruct at each depth above (and including)
diff --git a/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
index 2be5c931928408..f33b13047c8abf 100644
--- a/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
+++ b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
@@ -40,7 +40,7 @@ static llvm::cl::opt<bool> perFunctionTBAATrees(
// the number of TBAA access tag attributes attached to operations.
// It is set to kTagAttachmentUnlimited by default denoting "no limit".
static constexpr unsigned kTagAttachmentUnlimited =
- std::numeric_limits<unsigned>::max();
+ common::numeric_limits<unsigned>::max();
static llvm::cl::opt<unsigned>
tagAttachmentLimit("tbaa-attach-tag-max", llvm::cl::desc(""),
llvm::cl::init(kTagAttachmentUnlimited));
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index af924caf4504f7..cd54549e969b0c 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -1217,7 +1217,7 @@ unsigned fir::RecordType::getFieldIndex(llvm::StringRef ident) {
for (auto f : llvm::enumerate(getTypeList()))
if (ident == f.value().first)
return f.index();
- return std::numeric_limits<unsigned>::max();
+ return common::numeric_limits<unsigned>::max();
}
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Parser/token-parsers.h b/flang/lib/Parser/token-parsers.h
index fcb948ff0a71bc..2a09b9dd294cd0 100644
--- a/flang/lib/Parser/token-parsers.h
+++ b/flang/lib/Parser/token-parsers.h
@@ -394,12 +394,12 @@ struct DigitString64 {
bool overflow{false};
static constexpr auto getDigit{attempt(digit)};
while (auto nextDigit{getDigit.Parse(state)}) {
- if (value > std::numeric_limits<std::uint64_t>::max() / 10) {
+ if (value > common::numeric_limits<std::uint64_t>::max() / 10) {
overflow = true;
}
value *= 10;
int digitValue = **nextDigit - '0';
- if (value > std::numeric_limits<std::uint64_t>::max() - digitValue) {
+ if (value > common::numeric_limits<std::uint64_t>::max() - digitValue) {
overflow = true;
}
value += digitValue;
@@ -422,7 +422,7 @@ static std::optional<std::int64_t> SignedInteger(
if (!x) {
return std::nullopt;
}
- std::uint64_t limit{std::numeric_limits<std::int64_t>::max()};
+ std::uint64_t limit{common::numeric_limits<std::int64_t>::max()};
if (negate) {
limit = -(limit + 1);
}
@@ -465,12 +465,12 @@ struct DigitStringIgnoreSpaces {
bool overflow{false};
static constexpr auto getDigit{space >> attempt(digit)};
while (auto nextDigit{getDigit.Parse(state)}) {
- if (value > std::numeric_limits<std::uint64_t>::max() / 10) {
+ if (value > common::numeric_limits<std::uint64_t>::max() / 10) {
overflow = true;
}
value *= 10;
int digitValue = **nextDigit - '0';
- if (value > std::numeric_limits<std::uint64_t>::max() - digitValue) {
+ if (value > common::numeric_limits<std::uint64_t>::max() - digitValue) {
overflow = true;
}
value += digitValue;
diff --git a/flang/lib/Semantics/compute-offsets.cpp b/flang/lib/Semantics/compute-offsets.cpp
index 06d9c50ecc6eb4..ce9e19c57a86c9 100644
--- a/flang/lib/Semantics/compute-offsets.cpp
+++ b/flang/lib/Semantics/compute-offsets.cpp
@@ -31,7 +31,7 @@ static_assert(sizeof(std::size_t) >= sizeof(std::int64_t),
"byte sizes and offsets are accumulated in std::size_t and must not be "
"narrowed");
static constexpr std::size_t maxStorageSizeInBytes{
- static_cast<std::size_t>(std::numeric_limits<std::int64_t>::max())};
+ static_cast<std::size_t>(common::numeric_limits<std::int64_t>::max())};
static bool IsTooBig(std::size_t bytes) {
return bytes > maxStorageSizeInBytes;
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index e3ed0e0be52d4b..ff701965202c87 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -2992,7 +2992,7 @@ static bool CheckCompatibleArguments(
return true;
}
-static constexpr int cudaInfMatchingValue{std::numeric_limits<int>::max()};
+static constexpr int cudaInfMatchingValue{common::numeric_limits<int>::max()};
struct CudaMatchingDistance {
std::vector<int> perArg;
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index a3658af5e345e4..e2360fc20f954b 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1310,9 +1310,8 @@ bool IsTransparentInterveningCode(const parser::ExecutionPartConstruct &x) {
}
template <typename T,
- typename = std::enable_if_t<
- common::numeric_limits<llvm::remove_cvref_t<T>>::is_specialized>>
-WithReason<T> operator+(const WithReason<T> &a, const WithReason<T> &b) {
+ typename = std::enable_if_t<common::is_arithmetic<T>> WithReason<T>
+ operator+(const WithReason<T> &a, const WithReason<T> &b) {
if (a.value && b.value) {
return WithReason<T>{
*a.value + *b.value, Reason().Append(a.reason).Append(b.reason)};
@@ -1321,9 +1320,8 @@ WithReason<T> operator+(const WithReason<T> &a, const WithReason<T> &b) {
}
template <typename T,
- typename = std::enable_if_t<
- common::numeric_limits<llvm::remove_cvref_t<T>>::is_specialized>>
-WithReason<T> operator+(T a, const WithReason<T> &b) {
+ typename = std::enable_if_t<common::is_arithmetic<T>> WithReason<T>
+ operator+(T a, const WithReason<T> &b) {
return WithReason<T>{a, Reason()} + b;
}
diff --git a/flang/unittests/Evaluate/uint128.cpp b/flang/unittests/Evaluate/uint128.cpp
index 00e65adea2bbd4..44a28f6f915f37 100644
--- a/flang/unittests/Evaluate/uint128.cpp
+++ b/flang/unittests/Evaluate/uint128.cpp
@@ -1,9 +1,9 @@
#define AVOID_NATIVE_UINT128_T 1
#include "flang/Common/uint128.h"
+#include "flang/Common/numeric-limits.h"
#include "flang/Testing/testing.h"
#include "llvm/Support/raw_ostream.h"
#include <cinttypes>
-#include <limits>
#if (defined __GNUC__ || defined __clang__) && defined __SIZEOF_INT128__
#define HAS_NATIVE_UINT128_T 1
@@ -114,13 +114,15 @@ static void TestVsNative() {
}
#endif
-template <typename T> static void TestNumericLimitsEquivalence() {
- using Std = std::numeric_limits<T>;
+/// T is the native __(u)int128_t implementation to compare against. It only
+/// executes on platforms with HAS_NATIVE_UINT128_T enabled.
+template <typename T> static void TestNumericLimitsVsNative() {
+ using Std = common::numeric_limits<T>;
using Detail = Fortran::common::detail::numeric_limits<T>;
using Common = Fortran::common::numeric_limits<T>;
// Our implementation in Detail must be equivalent to Std
- // (if Std supports __int128).
+ // (if Std supports __(u)int128).
if constexpr (Std::is_specialized) {
TEST(Detail::is_specialized == Std::is_specialized);
TEST(Detail::is_signed == Std::is_signed);
@@ -129,11 +131,12 @@ template <typename T> static void TestNumericLimitsEquivalence() {
TEST(Detail::max() == Std::max());
TEST(Detail::lowest() == Std::lowest());
} else {
- llvm::outs() << "std::numeric_limits is not specialized for this type\n";
+ llvm::outs() << "common::numeric_limits is not specialized for this type\n";
}
// Common may refer to either Std to Detail; in either case it must be
- // identical.
+ // identical. This is testing the `using numeric_limits` junction between
+ // `detail::` and `std::`.
TEST(Detail::is_specialized == Common::is_specialized);
TEST(Detail::is_integer == Common::is_integer);
TEST(Detail::is_signed == Common::is_signed);
@@ -154,10 +157,10 @@ int main() {
#if HAS_NATIVE_UINT128_T
llvm::outs() << "Environment has native __uint128_t\n";
TestVsNative();
- TestNumericLimitsEquivalence<__int128_t>();
- TestNumericLimitsEquivalence<__uint128_t>();
- TestNumericLimitsEquivalence<const __int128_t>();
- TestNumericLimitsEquivalence<const __uint128_t>();
+ TestNumericLimitsVsNative<__int128_t>();
+ TestNumericLimitsVsNative<__uint128_t>();
+ TestNumericLimitsVsNative<const __int128_t>();
+ TestNumericLimitsVsNative<const __uint128_t>();
#else
llvm::outs() << "Environment lacks native __uint128_t\n";
#endif
>From 672ef9170992b32e60d633b897cbd2c29f1ffb5d Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 6 Oct 2026 18:57:12 +0200
Subject: [PATCH 10/15] Build fix
---
flang/lib/Semantics/openmp-utils.cpp | 5 +++--
flang/unittests/Evaluate/uint128.cpp | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index e2360fc20f954b..18bf76a29b3c72 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -17,6 +17,7 @@
#include "flang/Common/Fortran-consts.h"
#include "flang/Common/idioms.h"
#include "flang/Common/indirection.h"
+#include "flang/Common/numeric-limits.h"
#include "flang/Common/reference.h"
#include "flang/Common/visit.h"
#include "flang/Evaluate/check-expression.h"
@@ -1310,7 +1311,7 @@ bool IsTransparentInterveningCode(const parser::ExecutionPartConstruct &x) {
}
template <typename T,
- typename = std::enable_if_t<common::is_arithmetic<T>> WithReason<T>
+ typename = std::enable_if_t<common::is_arithmetic_v<T>>> WithReason<T>
operator+(const WithReason<T> &a, const WithReason<T> &b) {
if (a.value && b.value) {
return WithReason<T>{
@@ -1320,7 +1321,7 @@ template <typename T,
}
template <typename T,
- typename = std::enable_if_t<common::is_arithmetic<T>> WithReason<T>
+ typename = std::enable_if_t<common::is_arithmetic_v<T>>> WithReason<T>
operator+(T a, const WithReason<T> &b) {
return WithReason<T>{a, Reason()} + b;
}
diff --git a/flang/unittests/Evaluate/uint128.cpp b/flang/unittests/Evaluate/uint128.cpp
index 44a28f6f915f37..e6246e511d7696 100644
--- a/flang/unittests/Evaluate/uint128.cpp
+++ b/flang/unittests/Evaluate/uint128.cpp
@@ -117,7 +117,7 @@ static void TestVsNative() {
/// T is the native __(u)int128_t implementation to compare against. It only
/// executes on platforms with HAS_NATIVE_UINT128_T enabled.
template <typename T> static void TestNumericLimitsVsNative() {
- using Std = common::numeric_limits<T>;
+ using Std = std::numeric_limits<T>;
using Detail = Fortran::common::detail::numeric_limits<T>;
using Common = Fortran::common::numeric_limits<T>;
>From 99be34a633ccd7e9a93406560fa2804599bf0510 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 6 Oct 2026 19:25:55 +0200
Subject: [PATCH 11/15] clang-format
---
flang/lib/Semantics/openmp-utils.cpp | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 18bf76a29b3c72..1501965b1126e6 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1310,9 +1310,8 @@ bool IsTransparentInterveningCode(const parser::ExecutionPartConstruct &x) {
parser::Unwrap<parser::ContinueStmt>(x);
}
-template <typename T,
- typename = std::enable_if_t<common::is_arithmetic_v<T>>> WithReason<T>
- operator+(const WithReason<T> &a, const WithReason<T> &b) {
+template <typename T, typename = std::enable_if_t<common::is_arithmetic_v<T>>>
+WithReason<T> operator+(const WithReason<T> &a, const WithReason<T> &b) {
if (a.value && b.value) {
return WithReason<T>{
*a.value + *b.value, Reason().Append(a.reason).Append(b.reason)};
@@ -1320,9 +1319,8 @@ template <typename T,
return WithReason<T>();
}
-template <typename T,
- typename = std::enable_if_t<common::is_arithmetic_v<T>>> WithReason<T>
- operator+(T a, const WithReason<T> &b) {
+template <typename T, typename = std::enable_if_t<common::is_arithmetic_v<T>>>
+WithReason<T> operator+(T a, const WithReason<T> &b) {
return WithReason<T>{a, Reason()} + b;
}
>From 61a5fd1ead5651a332661f5ba04b4dc9e924c8f2 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 6 Oct 2026 19:32:43 +0200
Subject: [PATCH 12/15] Build fix
---
flang/include/flang/Optimizer/Dialect/FIROps.td | 2 +-
flang/lib/Optimizer/Dialect/FIRType.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 6dadf2eb7b4888..e6cd908dfab556 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -1660,7 +1660,7 @@ def fir_CoordinateOp
"mlir::Value":$ref, "llvm::ArrayRef<fir::IntOrValue>":$coor)>
];
let extraClassDeclaration = [{
- constexpr static int32_t kDynamicIndex = common::numeric_limits<int32_t>::min();
+ constexpr static int32_t kDynamicIndex = Fortran::common::numeric_limits<int32_t>::min();
CoordinateIndicesAdaptor getIndices();
// FortranObjectViewOpInterface methods:
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index cd54549e969b0c..dded9a7bab8e9b 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -1217,7 +1217,7 @@ unsigned fir::RecordType::getFieldIndex(llvm::StringRef ident) {
for (auto f : llvm::enumerate(getTypeList()))
if (ident == f.value().first)
return f.index();
- return common::numeric_limits<unsigned>::max();
+ return Fortran::common::numeric_limits<unsigned>::max();
}
//===----------------------------------------------------------------------===//
>From e7acf95042589eeb91c9b85eae7cc0e782b7b698 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 6 Oct 2026 20:15:30 +0200
Subject: [PATCH 13/15] Build fix
---
flang/lib/Lower/Bridge.cpp | 2 +-
flang/lib/Lower/ConvertConstant.cpp | 2 +-
flang/lib/Optimizer/CodeGen/TBAABuilder.cpp | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 792b4851e560aa..30f198e60dc307 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -4895,7 +4895,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
llvm::SmallVector<mlir::Attribute> attrList;
llvm::SmallVector<mlir::Block *> blockList;
unsigned typeGuardIdx = 0;
- std::size_t defaultAttrPos = common::numeric_limits<size_t>::max();
+ std::size_t defaultAttrPos = Fortran::common::numeric_limits<size_t>::max();
bool hasLocalScope = false;
llvm::SmallVector<const Fortran::semantics::Scope *> typeCaseScopes;
diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp
index 9260db58d1a868..642937da7bc5ed 100644
--- a/flang/lib/Lower/ConvertConstant.cpp
+++ b/flang/lib/Lower/ConvertConstant.cpp
@@ -753,7 +753,7 @@ genArrayLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
Fortran::evaluate::ConstantSubscript size =
Fortran::evaluate::GetSize(con.shape());
- if (size > common::numeric_limits<std::uint32_t>::max())
+ if (size > Fortran::common::numeric_limits<std::uint32_t>::max())
// llvm::SmallVector has limited size
TODO(loc, "Creation of very large array constants");
fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
diff --git a/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
index f33b13047c8abf..7f60acb70f50e1 100644
--- a/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
+++ b/flang/lib/Optimizer/CodeGen/TBAABuilder.cpp
@@ -40,7 +40,7 @@ static llvm::cl::opt<bool> perFunctionTBAATrees(
// the number of TBAA access tag attributes attached to operations.
// It is set to kTagAttachmentUnlimited by default denoting "no limit".
static constexpr unsigned kTagAttachmentUnlimited =
- common::numeric_limits<unsigned>::max();
+ Fortran::common::numeric_limits<unsigned>::max();
static llvm::cl::opt<unsigned>
tagAttachmentLimit("tbaa-attach-tag-max", llvm::cl::desc(""),
llvm::cl::init(kTagAttachmentUnlimited));
>From be54bc246fff4d2525862c98717e55c7bf06c769 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 6 Oct 2026 21:49:52 +0200
Subject: [PATCH 14/15] Build fixes
---
flang-rt/unittests/Runtime/Complex.cpp | 12 +++++++-----
flang-rt/unittests/Runtime/Reduction.cpp | 3 ++-
flang/lib/Optimizer/Dialect/FIRType.cpp | 1 +
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/flang-rt/unittests/Runtime/Complex.cpp b/flang-rt/unittests/Runtime/Complex.cpp
index 03b10021a279b4..64c86a75bd67a0 100644
--- a/flang-rt/unittests/Runtime/Complex.cpp
+++ b/flang-rt/unittests/Runtime/Complex.cpp
@@ -8,19 +8,21 @@
#include "gmock/gmock.h"
#include "gtest/gtest-matchers.h"
-#include <limits>
#ifdef __clang__
#pragma clang diagnostic ignored "-Wc99-extensions"
#endif
#include "flang/Common/Fortran-consts.h"
+#include "flang/Common/numeric-limits.h"
#include "flang/Runtime/cpp-type.h"
#include "flang/Runtime/entry-names.h"
#include <complex>
#include <cstdint>
+using Fortran::common::numeric_limits;
+
#ifndef _MSC_VER
#include <complex.h>
typedef float _Complex float_Complex_t;
@@ -113,7 +115,7 @@ TEST(Complex, cpowi) {
EXPECT_COMPLEX_FLOAT_EQ(cpowi(0.5f + 0.6if, -5), -1.121837f + 3.252915if);
EXPECT_COMPLEX_FLOAT_EQ(
- cpowi(0.f + 1if, common::numeric_limits<std::int32_t>::min()), 1.f + 0if);
+ cpowi(0.f + 1if, numeric_limits<std::int32_t>::min()), 1.f + 0if);
}
TEST(Complex, cpowk) {
@@ -131,7 +133,7 @@ TEST(Complex, cpowk) {
EXPECT_COMPLEX_FLOAT_EQ(cpowk(0.5f + 0.6if, -5), -1.121837f + 3.252915if);
EXPECT_COMPLEX_FLOAT_EQ(
- cpowk(0.f + 1if, common::numeric_limits<std::int64_t>::min()), 1.f + 0if);
+ cpowk(0.f + 1if, numeric_limits<std::int64_t>::min()), 1.f + 0if);
}
TEST(Complex, zpowi) {
@@ -149,7 +151,7 @@ TEST(Complex, zpowi) {
EXPECT_COMPLEX_DOUBLE_EQ(zpowi(0.5 + 0.6i, -5), -1.12183773 + 3.25291503i);
EXPECT_COMPLEX_DOUBLE_EQ(
- zpowi(0. + 1i, common::numeric_limits<std::int32_t>::min()), 1. + 0i);
+ zpowi(0. + 1i, numeric_limits<std::int32_t>::min()), 1. + 0i);
}
TEST(Complex, zpowk) {
@@ -167,5 +169,5 @@ TEST(Complex, zpowk) {
EXPECT_COMPLEX_DOUBLE_EQ(zpowk(0.5 + 0.6i, -5), -1.12183773 + 3.25291503i);
EXPECT_COMPLEX_DOUBLE_EQ(
- zpowk(0. + 1i, common::numeric_limits<std::int64_t>::min()), 1. + 0i);
+ zpowk(0. + 1i, numeric_limits<std::int64_t>::min()), 1. + 0i);
}
diff --git a/flang-rt/unittests/Runtime/Reduction.cpp b/flang-rt/unittests/Runtime/Reduction.cpp
index 9ed42cb8c6a7b8..edf84a9472fc5d 100644
--- a/flang-rt/unittests/Runtime/Reduction.cpp
+++ b/flang-rt/unittests/Runtime/Reduction.cpp
@@ -12,6 +12,7 @@
#include "flang-rt/runtime/descriptor.h"
#include "flang-rt/runtime/type-code.h"
#include "flang/Common/float128.h"
+#include "flang/Common/numeric-limits.h"
#include "flang/Runtime/allocatable.h"
#include "flang/Runtime/cpp-type.h"
#include "flang/Runtime/reduce.h"
@@ -23,7 +24,7 @@
#include <vector>
using namespace Fortran::runtime;
-using Fortran::numeric_limits;
+using Fortran::common::numeric_limits;
using Fortran::common::TypeCategory;
TEST(Reductions, Int4Ops) {
diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp
index dded9a7bab8e9b..f3eba166a7420c 100644
--- a/flang/lib/Optimizer/Dialect/FIRType.cpp
+++ b/flang/lib/Optimizer/Dialect/FIRType.cpp
@@ -12,6 +12,7 @@
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Common/ISO_Fortran_binding_wrapper.h"
+#include "flang/Common/numeric-limits.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/Support/KindMapping.h"
>From 3ad4809c17f5a0021587459c0ce89b56d3b9f41a Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Wed, 7 Oct 2026 00:45:29 +0200
Subject: [PATCH 15/15] Fix typo
---
flang/unittests/Evaluate/uint128.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/unittests/Evaluate/uint128.cpp b/flang/unittests/Evaluate/uint128.cpp
index e6246e511d7696..81ffe90d942c35 100644
--- a/flang/unittests/Evaluate/uint128.cpp
+++ b/flang/unittests/Evaluate/uint128.cpp
@@ -134,7 +134,7 @@ template <typename T> static void TestNumericLimitsVsNative() {
llvm::outs() << "common::numeric_limits is not specialized for this type\n";
}
- // Common may refer to either Std to Detail; in either case it must be
+ // Common may refer to either Std or Detail; in either case it must be
// identical. This is testing the `using numeric_limits` junction between
// `detail::` and `std::`.
TEST(Detail::is_specialized == Common::is_specialized);
More information about the flang-commits
mailing list