[libcxx-commits] [libcxx] [libc++][math][c++17] P0226R1 - Mathematical Special Functions: infra + `std::assoc_laguerre` (PR #205649)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jun 26 09:27:55 PDT 2026


https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/205649

>From 7555447d01b3e6bd5127f4ffbfaeeb223cf9ea81 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:52:48 +0200
Subject: [PATCH 1/8] link against Boost.Math

---
 libcxx/CMakeLists.txt     | 8 ++++++++
 libcxx/src/CMakeLists.txt | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 845240d1b894c..845042f15495f 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -899,6 +899,14 @@ add_custom_target(cxx-test-depends
   COMMENT "Build dependencies required to run the libc++ test suite.")
 
 add_subdirectory(include)
+
+# Header-only Boost.Math (INTERFACE target `boost_math`). Lives outside the
+# libcxx source tree, so a binary dir must be supplied.
+add_subdirectory(
+  ${LLVM_THIRD_PARTY_DIR}/boost-math
+  ${LIBCXX_BINARY_DIR}/third-party/boost-math
+  EXCLUDE_FROM_ALL)
+
 add_subdirectory(src)
 add_subdirectory(utils)
 add_subdirectory(modules)
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index de7817ad69f26..c882446596a3e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -176,6 +176,7 @@ add_library(cxx_shared SHARED ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_shared PUBLIC cxx-headers runtimes-libc-shared
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_shared
   PROPERTIES
@@ -263,6 +264,7 @@ add_library(cxx_static STATIC ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
 target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
 target_link_libraries(cxx_static PUBLIC cxx-headers runtimes-libc-static
                                   PRIVATE ${LIBCXX_LIBRARIES}
+                                  PRIVATE boost_math
                                   PRIVATE libcxx-abi-static
                                   PRIVATE llvm-libc-common-utilities)
 set_target_properties(cxx_static

>From fe89c404516cfea93ab67939b48a15c74213cd59 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 20:53:37 +0200
Subject: [PATCH 2/8] add empty src/mathematical_special_functions.cpp

---
 libcxx/src/CMakeLists.txt                     |  1 +
 libcxx/src/mathematical_special_functions.cpp | 17 +++++++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 libcxx/src/mathematical_special_functions.cpp

diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index c882446596a3e..d2168decbfdba 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -29,6 +29,7 @@ set(LIBCXX_SOURCES
   include/ryu/ryu.h
   include/to_chars_floating_point.h
   include/from_chars_floating_point.h
+  mathematical_special_functions.cpp
   memory.cpp
   memory_resource.cpp
   new_handler.cpp
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
new file mode 100644
index 0000000000000..db07d61f89319
--- /dev/null
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -0,0 +1,17 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
+namespace {}
+
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+_LIBCPP_END_NAMESPACE_STD

>From 7fbc20f4bea5a80fdebfb22eadee50cb90698b46 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:06:04 +0200
Subject: [PATCH 3/8] add simple test for std::assoc_laguerref (fails atm)

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
new file mode 100644
index 0000000000000..1a12c8f81a16c
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Missing powl
+// XFAIL: LLVM-LIBC-FIXME
+
+// REQUIRES: std-at-least-c++17
+
+// <cmath>
+//
+// [sf.cmath.assoc.laguerre], associated Laguerre polynomials
+// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
+// float               assoc_laguerref(unsigned n, unsigned m, float x);
+// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
+
+#include <cmath>
+
+int main(int, char**) {
+  std::assoc_laguerref(0, 0, 0.0f);
+
+  return 0;
+}

>From c7e11e92b9c70fe1f319a8f959ce1c11cbbfa9cc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 21:52:09 +0200
Subject: [PATCH 4/8] handling MATH_ERRNO, MATH_ERREXCEPT via __sf_result

---
 libcxx/include/__math/special_functions.h | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 6b217852e2f8c..86b3ac9cb665e 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -15,7 +15,10 @@
 #include <__math/traits.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_integral.h>
+#include <cerrno>
+#include <cfenv>
 #include <limits>
+#include <math.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -25,6 +28,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 #if _LIBCPP_STD_VER >= 17
 
+namespace __math {
+template <class _Tp>
+struct __sf_result {
+  bool __domain_error;
+  _Tp __ret;
+
+  operator _Tp() const {
+#  if math_errhandling & MATH_ERRNO
+    if (__domain_error)
+      errno = EDOM;
+#  endif
+
+#  if math_errhandling & MATH_ERREXCEPT
+    if (__domain_error)
+      feraiseexcept(FE_INVALID);
+#  endif
+
+    return __ret;
+  }
+};
+} // namespace __math
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).

>From 23081681878b007cb50f1ed115ec316fa956b4cf Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:00:10 +0200
Subject: [PATCH 5/8] impl assoc_laguerref

---
 libcxx/include/__math/special_functions.h     |  9 +++++
 libcxx/src/mathematical_special_functions.cpp | 37 ++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/libcxx/include/__math/special_functions.h b/libcxx/include/__math/special_functions.h
index 86b3ac9cb665e..d0d6e56507e85 100644
--- a/libcxx/include/__math/special_functions.h
+++ b/libcxx/include/__math/special_functions.h
@@ -25,6 +25,7 @@
 #endif
 
 _LIBCPP_BEGIN_NAMESPACE_STD
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 #if _LIBCPP_STD_VER >= 17
 
@@ -48,8 +49,15 @@ struct __sf_result {
     return __ret;
   }
 };
+
+_LIBCPP_EXPORTED_FROM_ABI __sf_result<float> __assoc_laguerre(unsigned int, unsigned int, float) noexcept;
+
 } // namespace __math
 
+inline _LIBCPP_HIDE_FROM_ABI float assoc_laguerref(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return __math::__assoc_laguerre(__n, __m, __x);
+}
+
 template <class _Real>
 _LIBCPP_HIDE_FROM_ABI _Real __hermite(unsigned __n, _Real __x) {
   // The Hermite polynomial H_n(x).
@@ -104,6 +112,7 @@ _LIBCPP_HIDE_FROM_ABI double hermite(unsigned __n, _Integer __x) {
 
 #endif // _LIBCPP_STD_VER >= 17
 
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___MATH_SPECIAL_FUNCTIONS_H
diff --git a/libcxx/src/mathematical_special_functions.cpp b/libcxx/src/mathematical_special_functions.cpp
index db07d61f89319..e18e9c5f2797c 100644
--- a/libcxx/src/mathematical_special_functions.cpp
+++ b/libcxx/src/mathematical_special_functions.cpp
@@ -7,11 +7,46 @@
 //===----------------------------------------------------------------------===//
 
 #include <__config>
+#include <boost/math/special_functions.hpp>
+#include <cmath>
+#include <optional>
+#include <type_traits>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-namespace {}
+namespace __math {
+namespace {
+template <class _Ret>
+optional<_Ret> __check_nan() {
+  return nullopt;
+}
+
+template <class _Ret, class _Arg, class... _Args>
+optional<_Ret> __check_nan(_Arg __arg, _Args... __args) {
+  if constexpr (is_floating_point_v<_Arg>)
+    if (isnan(__arg))
+      return __arg;
+  return __check_nan<_Ret>(__args...);
+}
+
+template <class Func, class... _Args, class _Ret = std::invoke_result_t<Func, _Args...>>
+__sf_result<_Ret> invoke_boost_math(Func f, _Args... __args) {
+  if (auto __maybe_nan = __check_nan<_Ret>(__args...); __maybe_nan.has_value())
+    return {.__domain_error = false, .__ret = *__maybe_nan};
+
+  try {
+    return {.__domain_error = false, .__ret = f(__args...)};
+  } catch (...) {
+    return {.__domain_error = true, .__ret = numeric_limits<_Ret>::quiet_NaN()};
+  }
+}
+} // namespace
+
+__sf_result<float> __assoc_laguerre(unsigned int __n, unsigned int __m, float __x) noexcept {
+  return invoke_boost_math([&](auto... __args) { return boost::math::laguerre(__args...); }, __n, __m, __x);
+}
+} // namespace __math
 
 _LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
 _LIBCPP_END_NAMESPACE_STD

>From adb174a0e9d29b172b17bc9e0d781db0dff15d5c Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 23 Jun 2026 22:34:12 +0200
Subject: [PATCH 6/8] test assoc_laguerref

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp        | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index 1a12c8f81a16c..fe92daa501c6f 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -6,22 +6,18 @@
 //
 //===----------------------------------------------------------------------===//
 
-// Missing powl
-// XFAIL: LLVM-LIBC-FIXME
-
 // REQUIRES: std-at-least-c++17
 
 // <cmath>
 //
 // [sf.cmath.assoc.laguerre], associated Laguerre polynomials
-// floating-point-type assoc_laguerre(unsigned n, unsigned m, floating-point-type x);
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
-// long double         assoc_laguerrel(unsigned n, unsigned m, long double x);
 
+#include <cassert>
 #include <cmath>
 
-int main(int, char**) {
-  std::assoc_laguerref(0, 0, 0.0f);
-
-  return 0;
+int main() {
+  // Single value tested against known solution.
+  // Note, underlying Boost.Math is itself well-tested.
+  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
 }

>From b7bc1fb6247235ce62066d70a30c13608a8672fc Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:00 +0200
Subject: [PATCH 7/8] test: common.h

---
 .../std/numerics/c.math/sf.cmath/common.h     | 65 +++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 libcxx/test/std/numerics/c.math/sf.cmath/common.h

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/common.h b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
new file mode 100644
index 0000000000000..867e6af6f6914
--- /dev/null
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/common.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 TEST_SF_CMATH_COMMON_H
+#define TEST_SF_CMATH_COMMON_H
+
+#include <cassert>
+#include <cerrno>
+#include <cmath>
+#include <math.h>
+#include <cfenv>
+
+// std::type_identity is C++20 (we need to support C++17 here)
+template <class T>
+struct type_identity {
+  typedef T type;
+};
+template <class T>
+using type_identity_t = typename type_identity<T>::type;
+
+template <class T>
+bool between(type_identity_t<T> lower, T value, type_identity_t<T> upper) {
+  return lower < value && value < upper;
+}
+
+template <class Func>
+void check_no_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EACCES);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(!std::fetestexcept(FE_INVALID));
+#endif
+}
+
+template <class Func>
+void check_domain_error(Func f) {
+#if math_errhandling & MATH_ERRNO
+  errno = EACCES;
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  std::feclearexcept(FE_INVALID);
+#endif
+  f();
+#if math_errhandling & MATH_ERRNO
+  assert(errno == EDOM);
+#endif
+#if math_errhandling & MATH_ERREXCEPT
+  assert(std::fetestexcept(FE_INVALID));
+#endif
+}
+
+#endif // TEST_SF_CMATH_COMMON_H

>From a47f9e902e5c427c84115f4af119eedf8778e182 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Fri, 26 Jun 2026 18:26:59 +0200
Subject: [PATCH 8/8] more testing of assoc_laguerref: values, types, domain
 error

---
 .../c.math/sf.cmath/assoc_laguerre.pass.cpp   | 36 +++++++++++++++++--
 1 file changed, 33 insertions(+), 3 deletions(-)

diff --git a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
index fe92daa501c6f..eb747efb4c1c6 100644
--- a/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
+++ b/libcxx/test/std/numerics/c.math/sf.cmath/assoc_laguerre.pass.cpp
@@ -14,10 +14,40 @@
 // float               assoc_laguerref(unsigned n, unsigned m, float x);
 
 #include <cassert>
+#include <cerrno>
+#include <cfenv>
 #include <cmath>
 
+#include "common.h"
+#include "type_algorithms.h"
+
+struct TestFloatingPoint {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    assert(between(0.99f, std::assoc_laguerref(1, 1, T(1.)), 1.01f));
+
+    assert(between(-0.01f, std::assoc_laguerref(2, 2, T(2.)), 0.01f));
+
+    assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+
+    check_no_domain_error([] { (void)std::assoc_laguerref(0, 0, std::numeric_limits<T>::quiet_NaN()); });
+  }
+};
+
+struct TestIntegral {
+  template <class T>
+  void operator()() const {
+    assert(between(0.99f, std::assoc_laguerref(0, 0, T(0.)), 1.01f));
+
+    static_assert(std::is_same_v<decltype(std::assoc_laguerref(0, 0, T(0.))), float>);
+  }
+};
+
 int main() {
-  // Single value tested against known solution.
-  // Note, underlying Boost.Math is itself well-tested.
-  assert(std::abs(std::assoc_laguerref(2, 10, 0.5f) - 60.125f) < 0.001f);
+  types::for_each(types::floating_point_types{}, TestFloatingPoint{});
+  types::for_each(types::integral_types{}, TestIntegral{});
 }



More information about the libcxx-commits mailing list