[libcxx-commits] [libcxx] [libcxx][FreeBSD] Fix __transform_primary in FreeBSD (PR #186130)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 19 08:55:25 PDT 2026
https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/186130
>From 28c99ae55416b428da6b76c23fe44646e32aa65a Mon Sep 17 00:00:00 2001
From: aokblast <aokblast at FreeBSD.org>
Date: Sat, 6 Jun 2026 00:55:42 +0800
Subject: [PATCH] Fix __transform_primary in FreeBSD
FreeBSD's strxfrm() encodes collation weights one level at a time,
separating the primary, secondary, and tertiary with '.' bytes. Since
primary equivalence only depends on the primary collation weight, ignore
everything after the first separator when constructing the transformed
key.
This patch the intended behavior of primary equivalence and avoids
relying on glibc's fixed-size collation-key representation.
---
libcxx/include/regex | 23 +++++++++++++++++++
.../re/re.traits/transform_primary.pass.cpp | 1 -
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/regex b/libcxx/include/regex
index 695a0f21754c4..91968a5c87c74 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -1130,6 +1130,18 @@ typename regex_traits<_CharT>::string_type
regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, char) const {
const string_type __s(__f, __l);
string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+# if defined(__FreeBSD__)
+ // FreeBSD's strxfrm() emits the weights one collation level at a time,
+ // separating the levels with a byte ('.') that is chosen to sort below every
+ // weight byte. Primary equivalence only depends on the first (primary)
+ // level, so drop everything from the first separator onwards. This keeps
+ // characters that share a primary weight equal (e.g. 'A' and 'A-acute' in
+ // cs_CZ) even when the lower levels expand to a different number of weights,
+ // which the fixed-size logic below could not handle.
+ typename string_type::size_type __sep = __d.find('.');
+ if (__sep != string_type::npos)
+ __d.erase(__sep);
+# else
switch (__d.size()) {
case 1:
break;
@@ -1140,6 +1152,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+# endif
return __d;
}
@@ -1150,6 +1163,15 @@ typename regex_traits<_CharT>::string_type
regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator __l, wchar_t) const {
const string_type __s(__f, __l);
string_type __d = __col_->transform(__s.data(), __s.data() + __s.size());
+# if defined(__FreeBSD__)
+ // As in the char overload above, FreeBSD's wcsxfrm() separates the collation
+ // levels with a weight (the value 1) that sorts below every real weight.
+ // Keep only the primary level so primary-equivalent characters compare equal
+ // regardless of how many weights their lower levels expand to.
+ typename string_type::size_type __sep = __d.find(static_cast<_CharT>(1));
+ if (__sep != string_type::npos)
+ __d.erase(__sep);
+# else
switch (__d.size()) {
case 1:
break;
@@ -1160,6 +1182,7 @@ regex_traits<_CharT>::__transform_primary(_ForwardIterator __f, _ForwardIterator
__d.clear();
break;
}
+# endif
return __d;
}
# endif
diff --git a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
index 422e3a591e643..ff866e6031b4a 100644
--- a/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
+++ b/libcxx/test/std/re/re.traits/transform_primary.pass.cpp
@@ -10,7 +10,6 @@
// XFAIL: netbsd
// XFAIL: LIBCXX-AIX-FIXME
-// XFAIL: LIBCXX-FREEBSD-FIXME
// REQUIRES: locale.cs_CZ.ISO8859-2
More information about the libcxx-commits
mailing list