Skip to content

Conversation

@philnik777
Copy link
Contributor

@philnik777 philnik777 commented Dec 3, 2025

This fixes two bugs reported in #121795 and adds regression tests. Specifically, these bugs are in the base detection mechanism. The first bug is that the out parameter isn't set when the stream only contains zero and after that is the end of the stream. The second one is that we don't consider 0 to be a number, and instead we only parse it as the start of an octal literal.

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

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

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions h,cpp -- libcxx/include/__locale_dir/num.h libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp --diff_from_common_commit

⚠️
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing origin/main to the base branch/commit you want to compare against.
⚠️

View the diff from clang-format here.
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
index 45b034833..db9c4585d 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
@@ -670,101 +670,101 @@ int main(int, char**)
       assert(v == std::numeric_limits<long>::min());
     }
 
-  { // Check that auto-detection of the base works properly
-    ios.flags(ios.flags() & ~std::ios::basefield);
-    { // zeroes
-      {
-        v                          = -1;
-        const char str[]           = "0";
-        std::ios_base::iostate err = ios.goodbit;
+    { // Check that auto-detection of the base works properly
+      ios.flags(ios.flags() & ~std::ios::basefield);
+      { // zeroes
+        {
+          v                          = -1;
+          const char str[]           = "0";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 1), ios, err, v);
-        assert(base(iter) == str + 1);
-        assert(err == ios.eofbit);
-        assert(v == 0);
-      }
-      {
-        v                          = -1;
-        const char str[]           = "00";
-        std::ios_base::iostate err = ios.goodbit;
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 1), ios, err, v);
+          assert(base(iter) == str + 1);
+          assert(err == ios.eofbit);
+          assert(v == 0);
+        }
+        {
+          v                          = -1;
+          const char str[]           = "00";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
-        assert(base(iter) == str + 2);
-        assert(err == ios.eofbit);
-        assert(v == 0);
-      }
-      {
-        v                          = -1;
-        const char str[]           = "0x0";
-        std::ios_base::iostate err = ios.goodbit;
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
+          assert(base(iter) == str + 2);
+          assert(err == ios.eofbit);
+          assert(v == 0);
+        }
+        {
+          v                          = -1;
+          const char str[]           = "0x0";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
-        assert(base(iter) == str + 3);
-        assert(err == ios.eofbit);
-        assert(v == 0);
-      }
-      {
-        v                          = -1;
-        const char str[]           = "0X0";
-        std::ios_base::iostate err = ios.goodbit;
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+          assert(base(iter) == str + 3);
+          assert(err == ios.eofbit);
+          assert(v == 0);
+        }
+        {
+          v                          = -1;
+          const char str[]           = "0X0";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
-        assert(base(iter) == str + 3);
-        assert(err == ios.eofbit);
-        assert(v == 0);
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+          assert(base(iter) == str + 3);
+          assert(err == ios.eofbit);
+          assert(v == 0);
+        }
       }
-    }
-    { // first character after base is out of range
-      {
-        v                          = -1;
-        const char str[]           = "08";
-        std::ios_base::iostate err = ios.goodbit;
+      { // first character after base is out of range
+        {
+          v                          = -1;
+          const char str[]           = "08";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
-        assert(base(iter) == str + 1);
-        assert(err == ios.goodbit);
-        assert(v == 0);
-      }
-      {
-        v                          = -1;
-        const char str[]           = "1a";
-        std::ios_base::iostate err = ios.goodbit;
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
+          assert(base(iter) == str + 1);
+          assert(err == ios.goodbit);
+          assert(v == 0);
+        }
+        {
+          v                          = -1;
+          const char str[]           = "1a";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
-        assert(base(iter) == str + 1);
-        assert(err == ios.goodbit);
-        assert(v == 1);
-      }
-      {
-        v                          = -1;
-        const char str[]           = "0xg";
-        std::ios_base::iostate err = ios.goodbit;
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
+          assert(base(iter) == str + 1);
+          assert(err == ios.goodbit);
+          assert(v == 1);
+        }
+        {
+          v                          = -1;
+          const char str[]           = "0xg";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
-        assert(base(iter) == str + 2);
-        assert(err == ios.failbit);
-        assert(v == 0);
-      }
-      {
-        v                          = -1;
-        const char str[]           = "0Xg";
-        std::ios_base::iostate err = ios.goodbit;
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+          assert(base(iter) == str + 2);
+          assert(err == ios.failbit);
+          assert(v == 0);
+        }
+        {
+          v                          = -1;
+          const char str[]           = "0Xg";
+          std::ios_base::iostate err = ios.goodbit;
 
-        cpp17_input_iterator<const char*> iter =
-            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
-        assert(base(iter) == str + 2);
-        assert(err == ios.failbit);
-        assert(v == 0);
+          cpp17_input_iterator<const char*> iter =
+              f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+          assert(base(iter) == str + 2);
+          assert(err == ios.failbit);
+          assert(v == 0);
+        }
       }
     }
-  }
 
   return 0;
 }

@ldionne ldionne marked this pull request as ready for review December 3, 2025 15:12
@ldionne ldionne requested a review from a team as a code owner December 3, 2025 15:12
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 3, 2025

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

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

2 Files Affected:

  • (modified) libcxx/include/__locale_dir/num.h (+2)
  • (modified) libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp (+96)
diff --git a/libcxx/include/__locale_dir/num.h b/libcxx/include/__locale_dir/num.h
index 98b8eb0c600f5..b7ea02e7cb7f7 100644
--- a/libcxx/include/__locale_dir/num.h
+++ b/libcxx/include/__locale_dir/num.h
@@ -436,6 +436,7 @@ class num_get : public locale::facet, private __num_get<_CharT> {
         ++__first;
         if (__first == __last) {
           __err |= ios_base::eofbit;
+          __v = 0;
           return __first;
         }
         // __c2 == 'x' || __c2 == 'X'
@@ -444,6 +445,7 @@ class num_get : public locale::facet, private __num_get<_CharT> {
           ++__first;
         } else {
           __base = 8;
+          __parsed_num = true; // We only swallowed '0', so we've started to parse a number
         }
       } else {
         __base = 10;
diff --git a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
index a110aae2db11b..45b034833fecc 100644
--- a/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
+++ b/libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_long.pass.cpp
@@ -670,5 +670,101 @@ int main(int, char**)
       assert(v == std::numeric_limits<long>::min());
     }
 
+  { // Check that auto-detection of the base works properly
+    ios.flags(ios.flags() & ~std::ios::basefield);
+    { // zeroes
+      {
+        v                          = -1;
+        const char str[]           = "0";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 1), ios, err, v);
+        assert(base(iter) == str + 1);
+        assert(err == ios.eofbit);
+        assert(v == 0);
+      }
+      {
+        v                          = -1;
+        const char str[]           = "00";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
+        assert(base(iter) == str + 2);
+        assert(err == ios.eofbit);
+        assert(v == 0);
+      }
+      {
+        v                          = -1;
+        const char str[]           = "0x0";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+        assert(base(iter) == str + 3);
+        assert(err == ios.eofbit);
+        assert(v == 0);
+      }
+      {
+        v                          = -1;
+        const char str[]           = "0X0";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+        assert(base(iter) == str + 3);
+        assert(err == ios.eofbit);
+        assert(v == 0);
+      }
+    }
+    { // first character after base is out of range
+      {
+        v                          = -1;
+        const char str[]           = "08";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
+        assert(base(iter) == str + 1);
+        assert(err == ios.goodbit);
+        assert(v == 0);
+      }
+      {
+        v                          = -1;
+        const char str[]           = "1a";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 2), ios, err, v);
+        assert(base(iter) == str + 1);
+        assert(err == ios.goodbit);
+        assert(v == 1);
+      }
+      {
+        v                          = -1;
+        const char str[]           = "0xg";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+        assert(base(iter) == str + 2);
+        assert(err == ios.failbit);
+        assert(v == 0);
+      }
+      {
+        v                          = -1;
+        const char str[]           = "0Xg";
+        std::ios_base::iostate err = ios.goodbit;
+
+        cpp17_input_iterator<const char*> iter =
+            f.get(cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + 3), ios, err, v);
+        assert(base(iter) == str + 2);
+        assert(err == ios.failbit);
+        assert(v == 0);
+      }
+    }
+  }
+
   return 0;
 }

@alexfh
Copy link
Contributor

alexfh commented Dec 3, 2025

This fixes the problems we've seen so far. Thank you!

Copy link
Contributor

@alexfh alexfh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG. Thanks! This resolves the problems for us.

@philnik777 philnik777 merged commit c45dd43 into llvm:main Dec 4, 2025
82 of 84 checks passed
@philnik777 philnik777 deleted the num_get_fixes branch December 4, 2025 11:54
kcloudy0717 pushed a commit to kcloudy0717/llvm-project that referenced this pull request Dec 4, 2025
This fixes two bugs reported in llvm#121795 and adds regression tests.
Specifically, these bugs are in the base detection mechanism. The first
bug is that the out parameter isn't set when the stream only contains
zero and after that is the end of the stream. The second one is that we
don't consider `0` to be a number, and instead we only parse it as the
start of an octal literal.
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

None yet

Development

Successfully merging this pull request may close these issues.

4 participants