Skip to content

[C99] Remove the tgmath.h header #135236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

AaronBallman
Copy link
Collaborator

Clang provides a number of headers which are required because the compiler is the only part of the implementation which can provide the correct macro definitions. Things like <limits.h> or <stdint.h> are such headers.

<tgmath.h> is not something the compiler needs to provide macros for; it is the responsibility of the C Standard Library to provide these interfaces.

Historically, Clang has provided tgmath.h from a time when C11 was still new and not all C Standard Libraries (such as the one from MSVC) had correct support. Today, that's no longer the case. So this change removes <tgmath.h> from the set of headers Clang provides and it is now on the user to provide a conforming C Standard Library if they would like to use this functionality.

Clang provides a number of headers which are required because the
compiler is the only part of the implementation which can provide the
correct macro definitions. Things like <limits.h> or <stdint.h> are
such headers.

<tgmath.h> is not something the compiler needs to provide macros for;
it is the responsibility of the C Standard Library to provide these
interfaces.

Historically, Clang has provided tgmath.h from a time when C11 was
still new and not all C Standard Libraries (such as the one from MSVC)
had correct support. Today, that's no longer the case. So this change
removes <tgmath.h> from the set of headers Clang provides and it is now
on the user to provide a conforming C Standard Library if they would
like to use this functionality.
@AaronBallman AaronBallman added clang Clang issues not falling into any other category clang:headers Headers provided by Clang, e.g. for intrinsics c99 labels Apr 10, 2025
@llvmbot llvmbot added backend:X86 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules labels Apr 10, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 10, 2025

@llvm/pr-subscribers-backend-x86
@llvm/pr-subscribers-clang-modules

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

Changes

Clang provides a number of headers which are required because the compiler is the only part of the implementation which can provide the correct macro definitions. Things like <limits.h> or <stdint.h> are such headers.

<tgmath.h> is not something the compiler needs to provide macros for; it is the responsibility of the C Standard Library to provide these interfaces.

Historically, Clang has provided tgmath.h from a time when C11 was still new and not all C Standard Libraries (such as the one from MSVC) had correct support. Today, that's no longer the case. So this change removes <tgmath.h> from the set of headers Clang provides and it is now on the user to provide a conforming C Standard Library if they would like to use this functionality.


Patch is 36.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/135236.diff

10 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Headers/CMakeLists.txt (-1)
  • (modified) clang/lib/Headers/module.modulemap (-5)
  • (removed) clang/lib/Headers/tgmath.h (-1368)
  • (modified) clang/lib/Lex/ModuleMap.cpp (-1)
  • (removed) clang/test/Headers/tgmath-darwin.c (-12)
  • (removed) clang/test/Headers/tgmath.c (-38)
  • (modified) clang/test/Modules/Inputs/builtin-headers/system-modules.modulemap (-5)
  • (modified) clang/test/Modules/builtin-headers.mm (-1)
  • (modified) clang/www/c_status.html (-5)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 77bf3355af9da..f7324ab130545 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -122,6 +122,9 @@ C Language Changes
 - Clang now allows an ``inline`` specifier on a typedef declaration of a
   function type in Microsoft compatibility mode. #GH124869
 - Clang now allows ``restrict`` qualifier for array types with pointer elements (#GH92847).
+- Clang no longer provides the ``tgmath.h`` header as part of the compiler-
+  provided header files. This file needs to be provided by the C Standard
+  Library being compiled against.
 
 C2y Feature Support
 ^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Headers/CMakeLists.txt b/clang/lib/Headers/CMakeLists.txt
index acf49e40c447e..664c626590dc8 100644
--- a/clang/lib/Headers/CMakeLists.txt
+++ b/clang/lib/Headers/CMakeLists.txt
@@ -33,7 +33,6 @@ set(core_files
   __stddef_wint_t.h
   stdint.h
   stdnoreturn.h
-  tgmath.h
   unwind.h
   varargs.h
   )
diff --git a/clang/lib/Headers/module.modulemap b/clang/lib/Headers/module.modulemap
index dcaf09e8f2c55..3119aade1c957 100644
--- a/clang/lib/Headers/module.modulemap
+++ b/clang/lib/Headers/module.modulemap
@@ -305,11 +305,6 @@ module _Builtin_stdnoreturn [system] {
   export *
 }
 
-module _Builtin_tgmath [system] {
-  header "tgmath.h"
-  export *
-}
-
 module _Builtin_unwind [system] {
   header "unwind.h"
   export *
diff --git a/clang/lib/Headers/tgmath.h b/clang/lib/Headers/tgmath.h
deleted file mode 100644
index 7acf18b9dd357..0000000000000
--- a/clang/lib/Headers/tgmath.h
+++ /dev/null
@@ -1,1368 +0,0 @@
-/*===---- tgmath.h - Standard header for type generic math ----------------===*\
- *
- * 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 __CLANG_TGMATH_H
-#define __CLANG_TGMATH_H
-
-/* C99 7.22 Type-generic math <tgmath.h>. */
-#include <math.h>
-
-/*
- * Allow additional definitions and implementation-defined values on Apple
- * platforms. This is done after #include <math.h> to avoid depcycle conflicts
- * between libcxx and darwin in C++ modules builds.
- */
-#if defined(__APPLE__) && __STDC_HOSTED__ && __has_include_next(<tgmath.h>)
-#  include_next <tgmath.h>
-#else
-
-/* C++ handles type genericity with overloading in math.h. */
-#ifndef __cplusplus
-#include <complex.h>
-
-#define _TG_ATTRSp __attribute__((__overloadable__))
-#define _TG_ATTRS __attribute__((__overloadable__, __always_inline__))
-
-// promotion
-
-typedef void _Argument_type_is_not_arithmetic;
-static _Argument_type_is_not_arithmetic __tg_promote(...)
-  __attribute__((__unavailable__,__overloadable__));
-static double               _TG_ATTRSp __tg_promote(int);
-static double               _TG_ATTRSp __tg_promote(unsigned int);
-static double               _TG_ATTRSp __tg_promote(long);
-static double               _TG_ATTRSp __tg_promote(unsigned long);
-static double               _TG_ATTRSp __tg_promote(long long);
-static double               _TG_ATTRSp __tg_promote(unsigned long long);
-static float                _TG_ATTRSp __tg_promote(float);
-static double               _TG_ATTRSp __tg_promote(double);
-static long double          _TG_ATTRSp __tg_promote(long double);
-static float _Complex       _TG_ATTRSp __tg_promote(float _Complex);
-static double _Complex      _TG_ATTRSp __tg_promote(double _Complex);
-static long double _Complex _TG_ATTRSp __tg_promote(long double _Complex);
-
-#define __tg_promote1(__x)           (__typeof__(__tg_promote(__x)))
-#define __tg_promote2(__x, __y)      (__typeof__(__tg_promote(__x) + \
-                                                 __tg_promote(__y)))
-#define __tg_promote3(__x, __y, __z) (__typeof__(__tg_promote(__x) + \
-                                                 __tg_promote(__y) + \
-                                                 __tg_promote(__z)))
-
-// acos
-
-static float
-    _TG_ATTRS
-    __tg_acos(float __x) {return acosf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_acos(double __x) {return acos(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_acos(long double __x) {return acosl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_acos(float _Complex __x) {return cacosf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_acos(double _Complex __x) {return cacos(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_acos(long double _Complex __x) {return cacosl(__x);}
-
-#undef acos
-#define acos(__x) __tg_acos(__tg_promote1((__x))(__x))
-
-// asin
-
-static float
-    _TG_ATTRS
-    __tg_asin(float __x) {return asinf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_asin(double __x) {return asin(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_asin(long double __x) {return asinl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_asin(float _Complex __x) {return casinf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_asin(double _Complex __x) {return casin(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_asin(long double _Complex __x) {return casinl(__x);}
-
-#undef asin
-#define asin(__x) __tg_asin(__tg_promote1((__x))(__x))
-
-// atan
-
-static float
-    _TG_ATTRS
-    __tg_atan(float __x) {return atanf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_atan(double __x) {return atan(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_atan(long double __x) {return atanl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_atan(float _Complex __x) {return catanf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_atan(double _Complex __x) {return catan(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_atan(long double _Complex __x) {return catanl(__x);}
-
-#undef atan
-#define atan(__x) __tg_atan(__tg_promote1((__x))(__x))
-
-// acosh
-
-static float
-    _TG_ATTRS
-    __tg_acosh(float __x) {return acoshf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_acosh(double __x) {return acosh(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_acosh(long double __x) {return acoshl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_acosh(float _Complex __x) {return cacoshf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_acosh(double _Complex __x) {return cacosh(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_acosh(long double _Complex __x) {return cacoshl(__x);}
-
-#undef acosh
-#define acosh(__x) __tg_acosh(__tg_promote1((__x))(__x))
-
-// asinh
-
-static float
-    _TG_ATTRS
-    __tg_asinh(float __x) {return asinhf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_asinh(double __x) {return asinh(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_asinh(long double __x) {return asinhl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_asinh(float _Complex __x) {return casinhf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_asinh(double _Complex __x) {return casinh(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_asinh(long double _Complex __x) {return casinhl(__x);}
-
-#undef asinh
-#define asinh(__x) __tg_asinh(__tg_promote1((__x))(__x))
-
-// atanh
-
-static float
-    _TG_ATTRS
-    __tg_atanh(float __x) {return atanhf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_atanh(double __x) {return atanh(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_atanh(long double __x) {return atanhl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_atanh(float _Complex __x) {return catanhf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_atanh(double _Complex __x) {return catanh(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_atanh(long double _Complex __x) {return catanhl(__x);}
-
-#undef atanh
-#define atanh(__x) __tg_atanh(__tg_promote1((__x))(__x))
-
-// cos
-
-static float
-    _TG_ATTRS
-    __tg_cos(float __x) {return cosf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_cos(double __x) {return cos(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_cos(long double __x) {return cosl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_cos(float _Complex __x) {return ccosf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_cos(double _Complex __x) {return ccos(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_cos(long double _Complex __x) {return ccosl(__x);}
-
-#undef cos
-#define cos(__x) __tg_cos(__tg_promote1((__x))(__x))
-
-// sin
-
-static float
-    _TG_ATTRS
-    __tg_sin(float __x) {return sinf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_sin(double __x) {return sin(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_sin(long double __x) {return sinl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_sin(float _Complex __x) {return csinf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_sin(double _Complex __x) {return csin(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_sin(long double _Complex __x) {return csinl(__x);}
-
-#undef sin
-#define sin(__x) __tg_sin(__tg_promote1((__x))(__x))
-
-// tan
-
-static float
-    _TG_ATTRS
-    __tg_tan(float __x) {return tanf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_tan(double __x) {return tan(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_tan(long double __x) {return tanl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_tan(float _Complex __x) {return ctanf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_tan(double _Complex __x) {return ctan(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_tan(long double _Complex __x) {return ctanl(__x);}
-
-#undef tan
-#define tan(__x) __tg_tan(__tg_promote1((__x))(__x))
-
-// cosh
-
-static float
-    _TG_ATTRS
-    __tg_cosh(float __x) {return coshf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_cosh(double __x) {return cosh(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_cosh(long double __x) {return coshl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_cosh(float _Complex __x) {return ccoshf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_cosh(double _Complex __x) {return ccosh(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_cosh(long double _Complex __x) {return ccoshl(__x);}
-
-#undef cosh
-#define cosh(__x) __tg_cosh(__tg_promote1((__x))(__x))
-
-// sinh
-
-static float
-    _TG_ATTRS
-    __tg_sinh(float __x) {return sinhf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_sinh(double __x) {return sinh(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_sinh(long double __x) {return sinhl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_sinh(float _Complex __x) {return csinhf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_sinh(double _Complex __x) {return csinh(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_sinh(long double _Complex __x) {return csinhl(__x);}
-
-#undef sinh
-#define sinh(__x) __tg_sinh(__tg_promote1((__x))(__x))
-
-// tanh
-
-static float
-    _TG_ATTRS
-    __tg_tanh(float __x) {return tanhf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_tanh(double __x) {return tanh(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_tanh(long double __x) {return tanhl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_tanh(float _Complex __x) {return ctanhf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_tanh(double _Complex __x) {return ctanh(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_tanh(long double _Complex __x) {return ctanhl(__x);}
-
-#undef tanh
-#define tanh(__x) __tg_tanh(__tg_promote1((__x))(__x))
-
-// exp
-
-static float
-    _TG_ATTRS
-    __tg_exp(float __x) {return expf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_exp(double __x) {return exp(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_exp(long double __x) {return expl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_exp(float _Complex __x) {return cexpf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_exp(double _Complex __x) {return cexp(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_exp(long double _Complex __x) {return cexpl(__x);}
-
-#undef exp
-#define exp(__x) __tg_exp(__tg_promote1((__x))(__x))
-
-// log
-
-static float
-    _TG_ATTRS
-    __tg_log(float __x) {return logf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_log(double __x) {return log(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_log(long double __x) {return logl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_log(float _Complex __x) {return clogf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_log(double _Complex __x) {return clog(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_log(long double _Complex __x) {return clogl(__x);}
-
-#undef log
-#define log(__x) __tg_log(__tg_promote1((__x))(__x))
-
-// pow
-
-static float
-    _TG_ATTRS
-    __tg_pow(float __x, float __y) {return powf(__x, __y);}
-
-static double
-    _TG_ATTRS
-    __tg_pow(double __x, double __y) {return pow(__x, __y);}
-
-static long double
-    _TG_ATTRS
-    __tg_pow(long double __x, long double __y) {return powl(__x, __y);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_pow(float _Complex __x, float _Complex __y) {return cpowf(__x, __y);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_pow(double _Complex __x, double _Complex __y) {return cpow(__x, __y);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_pow(long double _Complex __x, long double _Complex __y)
-    {return cpowl(__x, __y);}
-
-#undef pow
-#define pow(__x, __y) __tg_pow(__tg_promote2((__x), (__y))(__x), \
-                               __tg_promote2((__x), (__y))(__y))
-
-// sqrt
-
-static float
-    _TG_ATTRS
-    __tg_sqrt(float __x) {return sqrtf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_sqrt(double __x) {return sqrt(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_sqrt(long double __x) {return sqrtl(__x);}
-
-static float _Complex
-    _TG_ATTRS
-    __tg_sqrt(float _Complex __x) {return csqrtf(__x);}
-
-static double _Complex
-    _TG_ATTRS
-    __tg_sqrt(double _Complex __x) {return csqrt(__x);}
-
-static long double _Complex
-    _TG_ATTRS
-    __tg_sqrt(long double _Complex __x) {return csqrtl(__x);}
-
-#undef sqrt
-#define sqrt(__x) __tg_sqrt(__tg_promote1((__x))(__x))
-
-// fabs
-
-static float
-    _TG_ATTRS
-    __tg_fabs(float __x) {return fabsf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_fabs(double __x) {return fabs(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_fabs(long double __x) {return fabsl(__x);}
-
-static float
-    _TG_ATTRS
-    __tg_fabs(float _Complex __x) {return cabsf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_fabs(double _Complex __x) {return cabs(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_fabs(long double _Complex __x) {return cabsl(__x);}
-
-#undef fabs
-#define fabs(__x) __tg_fabs(__tg_promote1((__x))(__x))
-
-// atan2
-
-static float
-    _TG_ATTRS
-    __tg_atan2(float __x, float __y) {return atan2f(__x, __y);}
-
-static double
-    _TG_ATTRS
-    __tg_atan2(double __x, double __y) {return atan2(__x, __y);}
-
-static long double
-    _TG_ATTRS
-    __tg_atan2(long double __x, long double __y) {return atan2l(__x, __y);}
-
-#undef atan2
-#define atan2(__x, __y) __tg_atan2(__tg_promote2((__x), (__y))(__x), \
-                                   __tg_promote2((__x), (__y))(__y))
-
-// cbrt
-
-static float
-    _TG_ATTRS
-    __tg_cbrt(float __x) {return cbrtf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_cbrt(double __x) {return cbrt(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_cbrt(long double __x) {return cbrtl(__x);}
-
-#undef cbrt
-#define cbrt(__x) __tg_cbrt(__tg_promote1((__x))(__x))
-
-// ceil
-
-static float
-    _TG_ATTRS
-    __tg_ceil(float __x) {return ceilf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_ceil(double __x) {return ceil(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_ceil(long double __x) {return ceill(__x);}
-
-#undef ceil
-#define ceil(__x) __tg_ceil(__tg_promote1((__x))(__x))
-
-// copysign
-
-static float
-    _TG_ATTRS
-    __tg_copysign(float __x, float __y) {return copysignf(__x, __y);}
-
-static double
-    _TG_ATTRS
-    __tg_copysign(double __x, double __y) {return copysign(__x, __y);}
-
-static long double
-    _TG_ATTRS
-    __tg_copysign(long double __x, long double __y) {return copysignl(__x, __y);}
-
-#undef copysign
-#define copysign(__x, __y) __tg_copysign(__tg_promote2((__x), (__y))(__x), \
-                                         __tg_promote2((__x), (__y))(__y))
-
-// erf
-
-static float
-    _TG_ATTRS
-    __tg_erf(float __x) {return erff(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_erf(double __x) {return erf(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_erf(long double __x) {return erfl(__x);}
-
-#undef erf
-#define erf(__x) __tg_erf(__tg_promote1((__x))(__x))
-
-// erfc
-
-static float
-    _TG_ATTRS
-    __tg_erfc(float __x) {return erfcf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_erfc(double __x) {return erfc(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_erfc(long double __x) {return erfcl(__x);}
-
-#undef erfc
-#define erfc(__x) __tg_erfc(__tg_promote1((__x))(__x))
-
-// exp2
-
-static float
-    _TG_ATTRS
-    __tg_exp2(float __x) {return exp2f(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_exp2(double __x) {return exp2(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_exp2(long double __x) {return exp2l(__x);}
-
-#undef exp2
-#define exp2(__x) __tg_exp2(__tg_promote1((__x))(__x))
-
-// expm1
-
-static float
-    _TG_ATTRS
-    __tg_expm1(float __x) {return expm1f(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_expm1(double __x) {return expm1(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_expm1(long double __x) {return expm1l(__x);}
-
-#undef expm1
-#define expm1(__x) __tg_expm1(__tg_promote1((__x))(__x))
-
-// fdim
-
-static float
-    _TG_ATTRS
-    __tg_fdim(float __x, float __y) {return fdimf(__x, __y);}
-
-static double
-    _TG_ATTRS
-    __tg_fdim(double __x, double __y) {return fdim(__x, __y);}
-
-static long double
-    _TG_ATTRS
-    __tg_fdim(long double __x, long double __y) {return fdiml(__x, __y);}
-
-#undef fdim
-#define fdim(__x, __y) __tg_fdim(__tg_promote2((__x), (__y))(__x), \
-                                 __tg_promote2((__x), (__y))(__y))
-
-// floor
-
-static float
-    _TG_ATTRS
-    __tg_floor(float __x) {return floorf(__x);}
-
-static double
-    _TG_ATTRS
-    __tg_floor(double __x) {return floor(__x);}
-
-static long double
-    _TG_ATTRS
-    __tg_floor(long double __x) {return floorl(__x);}
-
-#undef floor
-#define floor(__x) __tg_floor(__tg_promote1((__x))(__x))
-
-// fma
-
-static float
-    _TG_ATTRS
-    __tg_fma(float __x, float __y, float __z)
-    {return fmaf(__x, __y, __z);}
-
-static double
-    _TG_ATTRS
-    __tg_fma(double __x, double __y, double __z)
-    {return fma(__x, __y, __z);}
-
-static long double
-    _TG_ATTRS
-    __tg_fma(long double __x,long double __y, long double __z)
-    {return fmal(__x, __y, __z);}
-
-#undef fma
-#define fma(__x, __y, __z)                                \
-        __tg_fma(__tg_promote3((__x), (__y), (__z))(__x), \
-                 __tg_promote3((__x), (__y), (__z))(__y), \
-                 __tg_promote3((__x), (__y), (__z))(__z))
-
-// fmax
-
-static float
-    _TG_ATTRS
-    __tg_fmax(float __x, float __y) {return fmaxf(__x, __y);}
-
-static double
-    _TG_ATTRS
-    __tg_fmax(double __x, double __y) {return fmax(__x, __y);}
-
-static long double
-    _TG_ATTRS
-    __tg_fmax(long double __x, long double __y) {return fmaxl(__x, __y);}
-
-#undef fmax
-#define fmax(__x, __y) __tg_fmax(__tg_promote2((__x), (__y))(__x), \
-                                 __tg_promote2((__x), (__y))(__y))
-
-// fmin
-
-static float
-    _TG_ATTRS
-    __tg_fmin(float __x, float __y) {return fminf(__x, __y);}
-
-static double
-    _TG_ATTRS
-    __tg_fmin(double __x, double __y) {return fmin(__x, __y);}
-
-static long double
-    _TG_ATTRS
-    __tg_fmin(long double __x, long double __y) {return fminl(__x, __y);}
-
-#undef fmin
-#define fmin(__x, __y) __tg_fmin(__tg_promote2((__x), (__y))(__x), \
-                                 __tg_promote2((__x), (__y))(__y))
-
-// fmod
-
-static float
-    _TG_ATTRS
-    __tg_fmod(float __x, float __y) {return fmodf(__x, __y);}
-
-static double
-    _TG_ATTRS
-   ...
[truncated]

Copy link

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp -- clang/lib/Lex/ModuleMap.cpp
View the diff from clang-format here.
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
index f16fe414e..1c2fcaf07 100644
--- a/clang/lib/Lex/ModuleMap.cpp
+++ b/clang/lib/Lex/ModuleMap.cpp
@@ -253,17 +253,17 @@ OptionalFileEntryRef ModuleMap::findHeader(
 /// headers.
 static bool isBuiltinHeaderName(StringRef FileName) {
   return llvm::StringSwitch<bool>(FileName)
-           .Case("float.h", true)
-           .Case("iso646.h", true)
-           .Case("limits.h", true)
-           .Case("stdalign.h", true)
-           .Case("stdarg.h", true)
-           .Case("stdatomic.h", true)
-           .Case("stdbool.h", true)
-           .Case("stddef.h", true)
-           .Case("stdint.h", true)
-           .Case("unwind.h", true)
-           .Default(false);
+      .Case("float.h", true)
+      .Case("iso646.h", true)
+      .Case("limits.h", true)
+      .Case("stdalign.h", true)
+      .Case("stdarg.h", true)
+      .Case("stdatomic.h", true)
+      .Case("stdbool.h", true)
+      .Case("stddef.h", true)
+      .Case("stdint.h", true)
+      .Case("unwind.h", true)
+      .Default(false);
 }
 
 /// Determine whether the given module name is the name of a builtin

@AaronBallman
Copy link
Collaborator Author

Background: it's not clear to me which C Standard Library headers Clang should or should not provide, so perhaps this change is wrong for reasons I don't understand. However, we have a number of open bug reports about our implementation of tgmath.h, and there are further changes needed for C23 (like adding __STDC_VERSION_TGMATH_H__), so there's maintenance costs to our providing this header. I think where we eventually want to get to is that Clang provides only the headers it has to provide, but otherwise it's on the user to supply a conforming C Standard Library to compile against.

@hubert-reinterpretcast
Copy link
Collaborator

Today, that's no longer the case.

IBM's AIX ships a tgmath.h that only works with the IBM non-Clang-based XL compiler.

@AaronBallman
Copy link
Collaborator Author

Today, that's no longer the case.

IBM's AIX ships a tgmath.h that only works with the IBM non-Clang-based XL compiler.

Thank you for pointing that out! So IBM still needs Clang to vend this for now, I take it? Is IBM planning to update their tgmath.h so that it works with the Clang-based XL compiler (so we can eventually remove Clang's)?

@zygoloid
Copy link
Collaborator

Background: it's not clear to me which C Standard Library headers Clang should or should not provide,

Historically, the easiest line to draw has been that we aim to provide the freestanding headers. This matches GCC's documented position that it provides a complete conforming freestanding implementation, or the compiler for a hosted implementation.

For some reason, WG21 has trampled over that meaning of "freestanding" and we no longer have such a separation for C++, but it still exists for C, so it makes sense to still follow it there. And <tgmath.h> isn't a freestanding header.

There are sometimes other reasons why we might feel like we should be providing a header rather than leaving it to the C standard library implementation, and in particular <tgmath.h> used to be unimplementable in standard C, which was one such reason (we implemented it with __attribute__((overloadable)) before _Generic existed). But that's not been true for a long time. Another such reason is that the system header for the target doesn't work with Clang, which sounds like it is the case for AIX, so that's a good reason to keep it :)

I wonder if we can put it into an AIX-only (plus whatever other targets still need it) include directory, though, so we only find and use it on the targets where it's necessary?

@hubert-reinterpretcast
Copy link
Collaborator

Thank you for pointing that out! So IBM still needs Clang to vend this for now, I take it? Is IBM planning to update their tgmath.h so that it works with the Clang-based XL compiler (so we can eventually remove Clang's)?

The road to being able to retain the functionality with Clang while also removing the header from Clang is a long one. Enabling use of the system tgmath.h with Clang is "new functionality" that would only be made available for newer releases of AIX. The compiler is typically used on older releases that receive bug fixes but no new functionality.

@hubert-reinterpretcast
Copy link
Collaborator

I wonder if we can put it into an AIX-only (plus whatever other targets still need it) include directory, though, so we only find and use it on the targets where it's necessary?

That, or, generally #include_next except for certain platforms (like AIX)?

@zygoloid
Copy link
Collaborator

I wonder if we can put it into an AIX-only (plus whatever other targets still need it) include directory, though, so we only find and use it on the targets where it's necessary?

That, or, generally #include_next except for certain platforms (like AIX)?

Possibly. But if so, we should use a pattern like

#if we want our tgmath
#include <__clang_tgmath.h>
#else
#include_next <tgmath.h>
#endif

so we're not unnecessarily tokenizing the whole file, and so we can just put the __clang_tgmath.h in our module map to avoid needing to deal with layering issues on platforms that don't use it.

@hubert-reinterpretcast
Copy link
Collaborator

Does it make sense to keep the tests for all platforms then? If we decide to restrict the testing to the platforms expected to use the Clang-provided header, then I think it should at least be staged so that there is some CI cycle where the tests are run regardless (in case the system header is problematic for use with Clang on more platforms).

@perry-ca
Copy link
Contributor

Today, that's no longer the case.

IBM's AIX ships a tgmath.h that only works with the IBM non-Clang-based XL compiler.

Same for z/OS.

@efriedma-quic
Copy link
Collaborator

For some reason, WG21 has trampled over that meaning of "freestanding" and we no longer have such a separation for C++, but it still exists for C

See #132232.

@hubert-reinterpretcast
Copy link
Collaborator

hubert-reinterpretcast commented Apr 10, 2025

My attempt at doing, with Clang, what the glibc implementation of tgmath.h does (with new versions of GCC) also fails: https://godbolt.org/z/Eexaq3fqz

#include <math.h>
#include <complex.h>
int main(void) {
  return _Generic(__builtin_tgmath(
      sinf, sin, sinl, csinf, csin, csinl,
      0.L), default: 0);
}
<stdin>:4:19: error: use of unknown builtin '__builtin_tgmath' [-Wimplicit-function-declaration]
  return _Generic(__builtin_tgmath(
                  ^
1 error generated.

@zygoloid
Copy link
Collaborator

zygoloid commented Apr 10, 2025

I see. The glibc header comment gives an idea of what's going on here:

/* There are two variant implementations of type-generic macros in
   this file: one for GCC 8 and later, using __builtin_tgmath and
   where each macro expands each of its arguments only once, and one
   for older GCC, using other compiler extensions but with macros
   expanding their arguments many times (so resulting in exponential
   blowup of the size of expansions when calls to such macros are
   nested inside arguments to such macros).  */

Indeed, it seems that _Generic is not sufficient to implement the <tgmath.h> macros without expanding the arguments at least twice each, and in any case glibc doesn't have an implementation that uses _Generic.

So it looks like the world is unprepared for us to not provide our own <tgmath.h>. We could add a builtin matching the GCC __builtin_tgmath builtin and remove our header for at least those targets, though. And we should find out what other libc implementations are doing.

@zygoloid
Copy link
Collaborator

zygoloid commented Apr 10, 2025

A few datapoints:

But notably... those all suck. Every one of them macro expands the arguments repeatedly (leading to exponential code size for deeply nested calls) and expands to a pretty huge macro invocation. The current Clang implementation is better than all of these. Maybe that's sufficient reason to keep it.

@hubert-reinterpretcast
Copy link
Collaborator

We could add a builtin matching the GCC __builtin_tgmath builtin and remove our header for at least those targets, though.

Clang would either need to change its __GNUC__ value or the glibc header would need to add a __has_builtin query.
Oddly enough, __has_builtin(__builtin_tgmath) evaluates to zero for GCC.

@zygoloid
Copy link
Collaborator

For some reason, WG21 has trampled over that meaning of "freestanding" and we no longer have such a separation for C++, but it still exists for C

See #132232.

Sigh. So we can't use "freestanding" as the line between compiler and standard library anywhere any more, not even in C.

@h-vetinari
Copy link
Contributor

Clang would either need to change its __GNUC__ value

Independently of the discussion here, that value should IMO be updated (#42162) or at least documented (#99804)

@AaronBallman
Copy link
Collaborator Author

AaronBallman commented Apr 11, 2025

What I am taking away from this conversation is: 1) We need to keep tgmath.h, so this PR can be closed. 2) We should consider implementing __builtin_tgmath, 3) We should consider bumping the __GNUC__ version we report. I think all of those are sensible, though (2) seems more straightforward than (3).

Thank you all for the good discussion on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:X86 c99 clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:headers Headers provided by Clang, e.g. for intrinsics clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants