Skip to content

Commit c35da18

Browse files
[libc++] LWG2381: Inconsistency in parsing floating point numbers
This PR implements LWG2381 by rejecting 'i', 'I', 'n', 'N' in FP parsing, as inf and NaN are intendedly rejected by that LWG issue. The source character array used for parsing is "0123456789abcdefABCDEFxX+-pPiInN", whose first 26 or 28 characters are used for parsing integers or floating-point values respectively. Previously, libc++ used 32 characters, including 'i', 'I', 'n', 'N', for FP parsing, which was inconsistent with LWG2381. This PR also replaces magic numbers 26 and 28 (formerly 32) with named constants. As a driven-by change, some parts of get_long_double.pass.cpp are guarded with `LDBL_MANT_DIG >= 64`, because these parts fail on platforms where long double is of binary64 format (e.g. MSVC). Apple back-deployment targets remain broken due to dylib. XFAIL is marked in related tests.
1 parent 60ac394 commit c35da18

File tree

5 files changed

+91
-67
lines changed

5 files changed

+91
-67
lines changed

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
`3555 <https://wg21.link/LWG3555>`__,"``{transform,elements}_view::iterator::iterator_concept`` should consider const-qualification of the underlying range","June 2021","","","|ranges|"
9999
"","","","","",""
100100
`2191 <https://wg21.link/LWG2191>`__,"Incorrect specification of ``match_results(match_results&&)``","October 2021","|Nothing To Do|",""
101-
`2381 <https://wg21.link/LWG2381>`__,"Inconsistency in parsing floating point numbers","October 2021","",""
101+
`2381 <https://wg21.link/LWG2381>`__,"Inconsistency in parsing floating point numbers","October 2021","|Complete|","18.0"
102102
`2762 <https://wg21.link/LWG2762>`__,"``unique_ptr operator*()`` should be ``noexcept``","October 2021","",""
103103
`3121 <https://wg21.link/LWG3121>`__,"``tuple`` constructor constraints for ``UTypes&&...`` overloads","October 2021","",""
104104
`3123 <https://wg21.link/LWG3123>`__,"``duration`` constructor from representation shouldn't be effectively non-throwing","October 2021","","","|chrono|"

libcxx/include/locale

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,11 @@ _LIBCPP_HIDE_FROM_ABI _ForwardIterator __scan_keyword(
366366

367367
struct _LIBCPP_EXPORTED_FROM_ABI __num_get_base {
368368
static const int __num_get_buf_sz = 40;
369+
static const size_t __int_chr_cnt = 26; // count of leading characters in __src used for parsing integers
370+
static const size_t __fp_chr_cnt = 28; // count of leading characters in __src used for parsing floating-point values
369371

370372
static int __get_base(ios_base&);
371-
static const char __src[33];
373+
static const char __src[33]; // "0123456789abcdefABCDEFxX+-pPiInN"
372374
};
373375

374376
_LIBCPP_EXPORTED_FROM_ABI void
@@ -431,7 +433,7 @@ private:
431433
template <typename _Tp>
432434
const _Tp* __do_widen_p(ios_base& __iob, _Tp* __atoms) const {
433435
locale __loc = __iob.getloc();
434-
use_facet<ctype<_Tp> >(__loc).widen(__src, __src + 26, __atoms);
436+
use_facet<ctype<_Tp> >(__loc).widen(__src, __src + __int_chr_cnt, __atoms);
435437
return __atoms;
436438
}
437439

@@ -447,7 +449,7 @@ private:
447449
template <class _CharT>
448450
string __num_get<_CharT>::__stage2_int_prep(ios_base& __iob, _CharT* __atoms, _CharT& __thousands_sep) {
449451
locale __loc = __iob.getloc();
450-
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + 26, __atoms);
452+
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + __int_chr_cnt, __atoms);
451453
const numpunct<_CharT>& __np = std::use_facet<numpunct<_CharT> >(__loc);
452454
__thousands_sep = __np.thousands_sep();
453455
return __np.grouping();
@@ -458,7 +460,7 @@ template <class _CharT>
458460
string __num_get<_CharT>::__stage2_float_prep(
459461
ios_base& __iob, _CharT* __atoms, _CharT& __decimal_point, _CharT& __thousands_sep) {
460462
locale __loc = __iob.getloc();
461-
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + 32, __atoms);
463+
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + __fp_chr_cnt, __atoms);
462464
const numpunct<_CharT>& __np = std::use_facet<numpunct<_CharT> >(__loc);
463465
__decimal_point = __np.decimal_point();
464466
__thousands_sep = __np.thousands_sep();
@@ -490,7 +492,7 @@ __num_get<_CharT>::__stage2_int_loop(_CharT __ct, int __base, char* __a, char*&
490492
}
491493
return 0;
492494
}
493-
ptrdiff_t __f = std::find(__atoms, __atoms + 26, __ct) - __atoms;
495+
ptrdiff_t __f = std::find(__atoms, __atoms + __int_chr_cnt, __ct) - __atoms;
494496
if (__f >= 24)
495497
return -1;
496498
switch (__base) {
@@ -546,8 +548,8 @@ int __num_get<_CharT>::__stage2_float_loop(
546548
}
547549
return 0;
548550
}
549-
ptrdiff_t __f = std::find(__atoms, __atoms + 32, __ct) - __atoms;
550-
if (__f >= 32)
551+
ptrdiff_t __f = std::find(__atoms, __atoms + __num_get_base::__fp_chr_cnt, __ct) - __atoms;
552+
if (__f >= static_cast<ptrdiff_t>(__num_get_base::__fp_chr_cnt))
551553
return -1;
552554
char __x = __src[__f];
553555
if (__x == '-' || __x == '+') {
@@ -846,7 +848,7 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_signed(
846848
int __base = this->__get_base(__iob);
847849
// Stage 2
848850
char_type __thousands_sep;
849-
const int __atoms_size = 26;
851+
const int __atoms_size = __num_get_base::__int_chr_cnt;
850852
#ifdef _LIBCPP_ABI_OPTIMIZED_LOCALE_NUM_GET
851853
char_type __atoms1[__atoms_size];
852854
const char_type* __atoms = this->__do_widen(__iob, __atoms1);
@@ -895,7 +897,7 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_unsigned(
895897
int __base = this->__get_base(__iob);
896898
// Stage 2
897899
char_type __thousands_sep;
898-
const int __atoms_size = 26;
900+
const int __atoms_size = __num_get_base::__int_chr_cnt;
899901
#ifdef _LIBCPP_ABI_OPTIMIZED_LOCALE_NUM_GET
900902
char_type __atoms1[__atoms_size];
901903
const char_type* __atoms = this->__do_widen(__iob, __atoms1);
@@ -942,7 +944,7 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_floating_point(
942944
iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Fp& __v) const {
943945
// Stage 1, nothing to do
944946
// Stage 2
945-
char_type __atoms[32];
947+
char_type __atoms[__num_get_base::__fp_chr_cnt];
946948
char_type __decimal_point;
947949
char_type __thousands_sep;
948950
string __grouping = this->__stage2_float_prep(__iob, __atoms, __decimal_point, __thousands_sep);
@@ -996,10 +998,11 @@ _InputIterator num_get<_CharT, _InputIterator>::do_get(
996998
// Stage 1
997999
int __base = 16;
9981000
// Stage 2
999-
char_type __atoms[26];
1001+
char_type __atoms[__num_get_base::__int_chr_cnt];
10001002
char_type __thousands_sep = char_type();
10011003
string __grouping;
1002-
std::use_facet<ctype<_CharT> >(__iob.getloc()).widen(__num_get_base::__src, __num_get_base::__src + 26, __atoms);
1004+
std::use_facet<ctype<_CharT> >(__iob.getloc())
1005+
.widen(__num_get_base::__src, __num_get_base::__src + __num_get_base::__int_chr_cnt, __atoms);
10031006
string __buf;
10041007
__buf.resize(__buf.capacity());
10051008
char* __a = &__buf[0];

libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_double.pass.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
// The fix for LWG2381 (https://github.com/llvm/llvm-project/pull/77948) changed
10+
// behavior of FP parsing, while Apple back-deployment targets remain broken due
11+
// to the dylib.
12+
// XFAIL: stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{.+}}
13+
// XFAIL: stdlib=apple-libc++ && target={{.+}}-apple-macosx11.{{.+}}
14+
915
// <locale>
1016

1117
// class num_get<charT, InputIterator>
@@ -116,9 +122,9 @@ int main(int, char**)
116122
f.get(cpp17_input_iterator<const char*>(str),
117123
cpp17_input_iterator<const char*>(str+sizeof(str)),
118124
ios, err, v);
119-
assert(base(iter) == str+sizeof(str)-1);
120-
assert(err == ios.goodbit);
121-
assert(v == INFINITY);
125+
assert(base(iter) == str);
126+
assert(err == ios.failbit);
127+
assert(v == 0.0);
122128
}
123129
{
124130
const char str[] = "INF";
@@ -128,9 +134,9 @@ int main(int, char**)
128134
f.get(cpp17_input_iterator<const char*>(str),
129135
cpp17_input_iterator<const char*>(str+sizeof(str)),
130136
ios, err, v);
131-
assert(base(iter) == str+sizeof(str)-1);
132-
assert(err == ios.goodbit);
133-
assert(v == INFINITY);
137+
assert(base(iter) == str);
138+
assert(err == ios.failbit);
139+
assert(v == 0.0);
134140
}
135141
{
136142
const char str[] = "-inf";
@@ -140,9 +146,9 @@ int main(int, char**)
140146
f.get(cpp17_input_iterator<const char*>(str),
141147
cpp17_input_iterator<const char*>(str+sizeof(str)),
142148
ios, err, v);
143-
assert(base(iter) == str+sizeof(str)-1);
144-
assert(err == ios.goodbit);
145-
assert(v == -INFINITY);
149+
assert(base(iter) == str + 1);
150+
assert(err == ios.failbit);
151+
assert(v == 0.0);
146152
}
147153
{
148154
const char str[] = "-INF";
@@ -152,9 +158,9 @@ int main(int, char**)
152158
f.get(cpp17_input_iterator<const char*>(str),
153159
cpp17_input_iterator<const char*>(str+sizeof(str)),
154160
ios, err, v);
155-
assert(base(iter) == str+sizeof(str)-1);
156-
assert(err == ios.goodbit);
157-
assert(v == -INFINITY);
161+
assert(base(iter) == str + 1);
162+
assert(err == ios.failbit);
163+
assert(v == 0.0);
158164
}
159165
{
160166
const char str[] = "nan";
@@ -164,9 +170,9 @@ int main(int, char**)
164170
f.get(cpp17_input_iterator<const char*>(str),
165171
cpp17_input_iterator<const char*>(str+sizeof(str)),
166172
ios, err, v);
167-
assert(base(iter) == str+sizeof(str)-1);
168-
assert(err == ios.goodbit);
169-
assert(std::isnan(v));
173+
assert(base(iter) == str);
174+
assert(err == ios.failbit);
175+
assert(v == 0.0);
170176
}
171177
{
172178
const char str[] = "NAN";
@@ -176,9 +182,9 @@ int main(int, char**)
176182
f.get(cpp17_input_iterator<const char*>(str),
177183
cpp17_input_iterator<const char*>(str+sizeof(str)),
178184
ios, err, v);
179-
assert(base(iter) == str+sizeof(str)-1);
180-
assert(err == ios.goodbit);
181-
assert(std::isnan(v));
185+
assert(base(iter) == str);
186+
assert(err == ios.failbit);
187+
assert(v == 0.0);
182188
}
183189
{
184190
v = -1;

libcxx/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/get_float.pass.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
// The fix for LWG2381 (https://github.com/llvm/llvm-project/pull/77948) changed
10+
// behavior of FP parsing, while Apple back-deployment targets remain broken due
11+
// to the dylib.
12+
// XFAIL: stdlib=apple-libc++ && target={{.+}}-apple-macosx10.{{.+}}
13+
// XFAIL: stdlib=apple-libc++ && target={{.+}}-apple-macosx11.{{.+}}
14+
915
// <locale>
1016

1117
// class num_get<charT, InputIterator>
@@ -105,9 +111,9 @@ int main(int, char**)
105111
f.get(cpp17_input_iterator<const char*>(str),
106112
cpp17_input_iterator<const char*>(str+sizeof(str)),
107113
ios, err, v);
108-
assert(base(iter) == str+sizeof(str)-1);
109-
assert(err == ios.goodbit);
110-
assert(v == INFINITY);
114+
assert(base(iter) == str);
115+
assert(err == ios.failbit);
116+
assert(v == 0.0f);
111117
}
112118
{
113119
const char str[] = "INF";
@@ -117,9 +123,9 @@ int main(int, char**)
117123
f.get(cpp17_input_iterator<const char*>(str),
118124
cpp17_input_iterator<const char*>(str+sizeof(str)),
119125
ios, err, v);
120-
assert(base(iter) == str+sizeof(str)-1);
121-
assert(err == ios.goodbit);
122-
assert(v == INFINITY);
126+
assert(base(iter) == str);
127+
assert(err == ios.failbit);
128+
assert(v == 0.0f);
123129
}
124130
{
125131
const char str[] = "-inf";
@@ -129,9 +135,9 @@ int main(int, char**)
129135
f.get(cpp17_input_iterator<const char*>(str),
130136
cpp17_input_iterator<const char*>(str+sizeof(str)),
131137
ios, err, v);
132-
assert(base(iter) == str+sizeof(str)-1);
133-
assert(err == ios.goodbit);
134-
assert(v == -INFINITY);
138+
assert(base(iter) == str + 1);
139+
assert(err == ios.failbit);
140+
assert(v == 0.0f);
135141
}
136142
{
137143
const char str[] = "-INF";
@@ -141,9 +147,9 @@ int main(int, char**)
141147
f.get(cpp17_input_iterator<const char*>(str),
142148
cpp17_input_iterator<const char*>(str+sizeof(str)),
143149
ios, err, v);
144-
assert(base(iter) == str+sizeof(str)-1);
145-
assert(err == ios.goodbit);
146-
assert(v == -INFINITY);
150+
assert(base(iter) == str + 1);
151+
assert(err == ios.failbit);
152+
assert(v == 0.0f);
147153
}
148154
{
149155
const char str[] = "nan";
@@ -153,9 +159,9 @@ int main(int, char**)
153159
f.get(cpp17_input_iterator<const char*>(str),
154160
cpp17_input_iterator<const char*>(str+sizeof(str)),
155161
ios, err, v);
156-
assert(base(iter) == str+sizeof(str)-1);
157-
assert(err == ios.goodbit);
158-
assert(std::isnan(v));
162+
assert(base(iter) == str);
163+
assert(err == ios.failbit);
164+
assert(v == 0.0f);
159165
}
160166
{
161167
const char str[] = "NAN";
@@ -165,9 +171,9 @@ int main(int, char**)
165171
f.get(cpp17_input_iterator<const char*>(str),
166172
cpp17_input_iterator<const char*>(str+sizeof(str)),
167173
ios, err, v);
168-
assert(base(iter) == str+sizeof(str)-1);
169-
assert(err == ios.goodbit);
170-
assert(std::isnan(v));
174+
assert(base(iter) == str);
175+
assert(err == ios.failbit);
176+
assert(v == 0.0f);
171177
}
172178
{
173179
v = -1;

0 commit comments

Comments
 (0)