Skip to content

[libc++] Reduce the dependency of the locale base API on the base system from the headers #117764

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

Merged
merged 4 commits into from
Feb 20, 2025

Conversation

ldionne
Copy link
Member

@ldionne ldionne commented Nov 26, 2024

Many parts of the locale base API are only required when building the shared/static library, but not from the headers. Document those functions and carve out a few of those that don't work when _XOPEN_SOURCE is defined to something old.

Fixes #117630

@ldionne ldionne requested a review from a team as a code owner November 26, 2024 18:52
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Nov 26, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-libcxx

Author: Louis Dionne (ldionne)

Changes

Many parts of the locale base API are only required when building the shared/static library, but not from the headers. Document those functions and carve out a few of those that don't work when _XOPEN_SOURCE is defined to something old.

Fixes #117630


Full diff: https://github.com/llvm/llvm-project/pull/117764.diff

3 Files Affected:

  • (modified) libcxx/include/__locale_dir/locale_base_api.h (+11-6)
  • (modified) libcxx/include/__locale_dir/support/bsd_like.h (+4)
  • (added) libcxx/test/libcxx/xopen_source.gen.py (+41)
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index 8ed4c29cb8732f..89bb2a724fff09 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -23,13 +23,17 @@
 // Variadic functions may be implemented as templates with a parameter pack instead
 // of C-style variadic functions.
 //
+// Most of these functions are only required when building the library. Functions that are also
+// required when merely using the headers are marked as such below.
+//
 // TODO: I think __uselocale() is not necessary if we refactor a bit.
 // TODO: __localeconv shouldn't take a reference, but the Windows implementation doesn't allow copying __locale_t
+// TODO: Eliminate the need for any of these functions from the headers.
 //
 // Locale management
 // -----------------
 // namespace __locale {
-//  using __locale_t = implementation-defined;
+//  using __locale_t = implementation-defined;  // required by the headers
 //  __locale_t  __uselocale(__locale_t);
 //  __locale_t  __newlocale(int, const char*, __locale_t);
 //  void        __freelocale(__locale_t);
@@ -51,8 +55,8 @@
 // namespace __locale {
 //  int     __islower(int, __locale_t);
 //  int     __isupper(int, __locale_t);
-//  int     __isdigit(int, __locale_t);
-//  int     __isxdigit(int, __locale_t);
+//  int     __isdigit(int, __locale_t);  // required by the headers
+//  int     __isxdigit(int, __locale_t); // required by the headers
 //  int     __toupper(int, __locale_t);
 //  int     __tolower(int, __locale_t);
 //  int     __strcoll(const char*, const char*, __locale_t);
@@ -89,9 +93,10 @@
 //  int     __mbtowc(wchar_t*, const char*, size_t, __locale_t);
 //  size_t  __mbrlen(const char*, size_t, mbstate_t*, __locale_t);
 //  size_t  __mbsrtowcs(wchar_t*, const char**, size_t, mbstate_t*, __locale_t);
-//  int     __snprintf(char*, size_t, __locale_t, const char*, ...);
-//  int     __asprintf(char**, __locale_t, const char*, ...);
-//  int     __sscanf(const char*, __locale_t, const char*, ...);
+//
+//  int     __snprintf(char*, size_t, __locale_t, const char*, ...); // required by the headers
+//  int     __asprintf(char**, __locale_t, const char*, ...);        // required by the headers
+//  int     __sscanf(const char*, __locale_t, const char*, ...);     // required by the headers
 // }
 
 #if defined(__APPLE__)
diff --git a/libcxx/include/__locale_dir/support/bsd_like.h b/libcxx/include/__locale_dir/support/bsd_like.h
index cce6de64673b0e..ef9bf8c07543b2 100644
--- a/libcxx/include/__locale_dir/support/bsd_like.h
+++ b/libcxx/include/__locale_dir/support/bsd_like.h
@@ -144,19 +144,23 @@ inline _LIBCPP_HIDE_FROM_ABI wint_t __btowc(int __c, __locale_t __loc) { return
 
 inline _LIBCPP_HIDE_FROM_ABI int __wctob(wint_t __c, __locale_t __loc) { return ::wctob_l(__c, __loc); }
 
+#ifdef _LIBCPP_BUILDING_LIBRARY
 inline _LIBCPP_HIDE_FROM_ABI size_t
 __wcsnrtombs(char* __dest, const wchar_t** __src, size_t __nwc, size_t __len, mbstate_t* __ps, __locale_t __loc) {
   return ::wcsnrtombs_l(__dest, __src, __nwc, __len, __ps, __loc);
 }
+#endif
 
 inline _LIBCPP_HIDE_FROM_ABI size_t __wcrtomb(char* __s, wchar_t __wc, mbstate_t* __ps, __locale_t __loc) {
   return ::wcrtomb_l(__s, __wc, __ps, __loc);
 }
 
+#ifdef _LIBCPP_BUILDING_LIBRARY
 inline _LIBCPP_HIDE_FROM_ABI size_t
 __mbsnrtowcs(wchar_t* __dest, const char** __src, size_t __nms, size_t __len, mbstate_t* __ps, __locale_t __loc) {
   return ::mbsnrtowcs_l(__dest, __src, __nms, __len, __ps, __loc);
 }
+#endif
 
 inline _LIBCPP_HIDE_FROM_ABI size_t
 __mbrtowc(wchar_t* __pwc, const char* __s, size_t __n, mbstate_t* __ps, __locale_t __loc) {
diff --git a/libcxx/test/libcxx/xopen_source.gen.py b/libcxx/test/libcxx/xopen_source.gen.py
new file mode 100644
index 00000000000000..94298de0289fd3
--- /dev/null
+++ b/libcxx/test/libcxx/xopen_source.gen.py
@@ -0,0 +1,41 @@
+# ===----------------------------------------------------------------------===##
+#
+# 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
+#
+# ===----------------------------------------------------------------------===##
+
+# Make sure that libc++ headers work when defining _XOPEN_SOURCE=500.
+# We may not want to guarantee this forever, but since this works today and
+# it's something that users rely on, it makes sense to put a test on it.
+#
+# https://github.com/llvm/llvm-project/issues/117630
+
+# RUN: %{python} %s %{libcxx-dir}/utils
+
+import sys
+
+sys.path.append(sys.argv[1])
+from libcxx.header_information import (
+    lit_header_restrictions,
+    lit_header_undeprecations,
+    public_headers,
+)
+
+for header in public_headers:
+    for version in (500, 600, 700):
+        # TODO: <fstream> currently uses ::fseeko unguarded, which fails with _XOPEN_SOURCE=500.
+        if header == 'fstream' and version == 500:
+            continue
+
+        print(f"""\
+//--- {header}.xopen_source_{version}.compile.pass.cpp
+{lit_header_restrictions.get(header, '')}
+{lit_header_undeprecations.get(header, '')}
+
+// ADDITIONAL_COMPILE_FLAGS: -D_XOPEN_SOURCE={version}
+
+#include <{header}>
+"""
+        )

Copy link

github-actions bot commented Nov 26, 2024

✅ With the latest revision this PR passed the Python code formatter.

Copy link

github-actions bot commented Nov 26, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@ldionne
Copy link
Member Author

ldionne commented Nov 27, 2024

@nico Looking at the CI failures (on AIX, on FreeBSD and on macOS), it looks like much of the library was already broken when defining _XOPEN_SOURCE to older values. In particular, <fstream> makes use of fseeko (and I think it has been for a very long time).

I didn't think this was going to be my conclusion, but I am tempted to conclude that such legacy values for this macro might not be worth supporting. Doing it properly certainly seems to require a non-trivial amount of refactoring. Do you have thoughts?

@nico
Copy link
Contributor

nico commented Nov 27, 2024

As I said on the issue, it looks pretty common: https://grep.app/search?q=_XOPEN_SOURCE%20500&filter[lang][0]=C%2B%2B

I don't think calling the value "legacy" is terribly useful. People set it to some value because they want to use a function that was added at that value. I'm guessing they probably set it to either the value that is highest when they set it, or to the lowest value needed for whatever function they want – it's more a "set it to this value at least please". OTOH, it's probably also possible that some folks out there want to set it as a "set it to this at most please" if they want to deploy to older systems, I don't know.

The thing we hit in practice is that <chrono> used to work, and now it doesn't. If it's easy to fix that, might as well?

Since it was easy to work around in Chromium, I don't have a super strong opinion on this particular issue.

(I do have the opinion that libc++ generally tends to be not very friendly to update. We're currently 3 weeks behind again, just down from 5 weeks, due to at least 4 things that need some kind of tweak landing in fast succession. And this year, our libc++ autoroller needed significantly more handholding than last year. This isn't really the forum for that, though :) If there's interest, I can do an experience report writeup for this year – but I'm guessing this is all working as intended.)

@philnik777
Copy link
Contributor

(I do have the opinion that libc++ generally tends to be not very friendly to update. We're currently 3 weeks behind again, just down from 5 weeks, due to at least 4 things that need some kind of tweak landing in fast succession. And this year, our libc++ autoroller needed significantly more handholding than last year. This isn't really the forum for that, though :) If there's interest, I can do an experience report writeup for this year – but I'm guessing this is all working as intended.)

You are an early adopter, so that's not exactly surprising. It would be interesting to know where the hiccups are though.

@ldionne
Copy link
Member Author

ldionne commented Nov 27, 2024

As I said on the issue, it looks pretty common: https://grep.app/search?q=_XOPEN_SOURCE%20500&filter[lang][0]=C%2B%2B

Actually, almost all of the hits in a file named mutex.h (with the comment // may be needed to get the rwlock calls) seem to be coming from a copy of the same sources. So it looks like there's only 3x unique instances of it:

  • Abseil
  • mutex.h copied all over the place
  • two meson test cases

The thing we hit in practice is that <chrono> used to work, and now it doesn't. If it's easy to fix that, might as well?

The problem is that I don't know how to fix it "properly". I can certainly silence this specific error, but as seen in the the CI failures, "alternate" (to avoid the world "legacy") definitions of _XOPEN_SOURCE seems to break the code in various ways, and that's a pre-existing issue. So it's hard for me to put a test on it and make sure that we don't break this again in the future. Right now, the only alternative I can think of is to explicitly list all of the headers that are currently broken with _XOPEN_SOURCE=500 for each platform, and to carve out the test to UNSUPPORT those. That would prevent any backsliding of our _XOPEN_SOURCE=500 support (if we can call it that), but I wonder if that makes sense.

I guess we could do that as a stopgap until we refactor things such that we truly don't care about what that macro is defined to in the future, if that ever happens. I think that would require defining some symbols in the dylib (where we control the build flags).

(I do have the opinion that libc++ generally tends to be not very friendly to update. We're currently 3 weeks behind again, just down from 5 weeks, due to at least 4 things that need some kind of tweak landing in fast succession. And this year, our libc++ autoroller needed significantly more handholding than last year. This isn't really the forum for that, though :) If there's interest, I can do an experience report writeup for this year – but I'm guessing this is all working as intended.)

I'd be curious to know what roadblocks you encountered in your recent rolls. While you're an early adopter and updates can't be 0 work, we've been trying to provide transition paths for most potentially-breaking changes we make (partly as a result of past feedback like yours). I'd like to know in what cases that didn't work for you so we can either say "yeah, that one was intended" or improve in the future.

@ldionne ldionne force-pushed the review/fix-xopen-source branch from 1d1e32b to a486593 Compare February 4, 2025 21:52
@ldionne
Copy link
Member Author

ldionne commented Feb 4, 2025

It turns out this is breaking more code than I thought. I'll try to push this through the finish line and cherry-pick into LLVM 20.

@ldionne ldionne added this to the LLVM 20.X Release milestone Feb 4, 2025
@ldionne ldionne force-pushed the review/fix-xopen-source branch from a486593 to f7e6a7a Compare February 5, 2025 13:54
…tem from the headers

Many parts of the locale base API are only required when building the
shared/static library, but not from the headers. Document those functions
and carve out a few of those that don't work when _XOPEN_SOURCE is defined
to something old.

Fixes llvm#117630
@ldionne ldionne force-pushed the review/fix-xopen-source branch from a93cdc3 to 1c0610b Compare February 19, 2025 19:03
@ldionne ldionne merged commit f00b32e into llvm:main Feb 20, 2025
79 checks passed
@ldionne ldionne deleted the review/fix-xopen-source branch February 20, 2025 13:38
@ldionne
Copy link
Member Author

ldionne commented Feb 20, 2025

/cherry-pick f00b32e

@llvmbot
Copy link
Member

llvmbot commented Feb 20, 2025

Failed to cherry-pick: f00b32e

https://github.com/llvm/llvm-project/actions/runs/13436600368

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

@ldionne
Copy link
Member Author

ldionne commented Feb 20, 2025

/cherry-pick bcfd9f8 f00b32e

@llvmbot
Copy link
Member

llvmbot commented Feb 20, 2025

/pull-request #128009

swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Feb 21, 2025
…tem from the headers (llvm#117764)

Many parts of the locale base API are only required when building the
shared/static library, but not from the headers. Document those
functions and carve out a few of those that don't work when
_XOPEN_SOURCE is defined to something old.

Fixes llvm#117630

(cherry picked from commit f00b32e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
Development

Successfully merging this pull request may close these issues.

Regression: <chrono> does not compile on macOS with _XOPEN_SOURCE defined to a value in [500, 699]
4 participants