Author: ericwf Date: Wed Mar 28 18:18:53 2018 New Revision: 328751 URL: http://llvm.org/viewvc/llvm-project?rev=328751&view=rev Log: Fix PR36914 - num_get::get(unsigned) incorrectly handles negative numbers.
This patch corrects num_get for unsigned types to support strings with a leading `-` character. According to the standard the number should be parsed as an unsigned integer and then negated. Added: libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp Modified: libcxx/trunk/include/locale libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp Modified: libcxx/trunk/include/locale URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/locale?rev=328751&r1=328750&r2=328751&view=diff ============================================================================== --- libcxx/trunk/include/locale (original) +++ libcxx/trunk/include/locale Wed Mar 28 18:18:53 2018 @@ -768,10 +768,10 @@ __num_get_unsigned_integral(const char* { if (__a != __a_end) { - if (*__a == '-') - { - __err = ios_base::failbit; - return 0; + const bool __negate = *__a == '-'; + if (__negate && ++__a == __a_end) { + __err = ios_base::failbit; + return 0; } typename remove_reference<decltype(errno)>::type __save_errno = errno; errno = 0; @@ -785,13 +785,14 @@ __num_get_unsigned_integral(const char* __err = ios_base::failbit; return 0; } - else if (__current_errno == ERANGE || - numeric_limits<_Tp>::max() < __ll) + else if (__current_errno == ERANGE || numeric_limits<_Tp>::max() < __ll) { __err = ios_base::failbit; return numeric_limits<_Tp>::max(); } - return static_cast<_Tp>(__ll); + _Tp __res = static_cast<_Tp>(__ll); + if (__negate) __res = -__res; + return __res; } __err = ios_base::failbit; return 0; Modified: libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp?rev=328751&r1=328750&r2=328751&view=diff ============================================================================== --- libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp (original) +++ libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_min_max.pass.cpp Wed Mar 28 18:18:53 2018 @@ -15,9 +15,17 @@ using namespace std; +template <class T> +bool check_stream_failed(std::string const& val) { + istringstream ss(val); + T result; + return !(ss >> result); +} + template<typename T> void check_limits() { + const bool is_unsigned = std::is_unsigned<T>::value; T minv = numeric_limits<T>::min(); T maxv = numeric_limits<T>::max(); @@ -36,17 +44,12 @@ void check_limits() assert(new_minv == minv); assert(new_maxv == maxv); - if(mins == "0") - mins = "-1"; - else - mins[mins.size() - 1]++; - maxs[maxs.size() - 1]++; - - istringstream maxoss2(maxs), minoss2(mins); - - assert(! (maxoss2 >> new_maxv)); - assert(! (minoss2 >> new_minv)); + assert(check_stream_failed<T>(maxs)); + if (!is_unsigned) { + mins[mins.size() - 1]++; + assert(check_stream_failed<T>(mins)); + } } int main(void) Added: libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp?rev=328751&view=auto ============================================================================== --- libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp (added) +++ libcxx/trunk/test/std/localization/locale.categories/category.numeric/locale.num.get/facet.num.get.members/test_neg_one.pass.cpp Wed Mar 28 18:18:53 2018 @@ -0,0 +1,159 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// <locale> + +// class num_get<charT, InputIterator> + +// iter_type get(iter_type in, iter_type end, ios_base&, +// ios_base::iostate& err, unsigned int& v) const; + +#include <locale> +#include <ios> +#include <cassert> +#include <streambuf> +#include <sstream> +#include <iostream> +#include "test_iterators.h" + +typedef std::num_get<char, input_iterator<const char*> > F; + +class my_facet + : public F +{ +public: + explicit my_facet(std::size_t refs = 0) + : F(refs) {} +}; + +template <class T> +std::string make_neg_string(T value) { + std::ostringstream ss; + assert(ss << value); + std::string res = ss.str(); + return '-' + res; +} + +template <class T> +void test_neg_one() { + const my_facet f(1); + std::ios ios(0); + T v = static_cast<T>(42); + { + const char str[] = "-1"; + std::ios_base::iostate err = ios.goodbit; + input_iterator<const char*> iter = + f.get(input_iterator<const char*>(str), + input_iterator<const char*>(str+sizeof(str)), + ios, err, v); + assert(iter.base() == str+sizeof(str)-1); + assert(err == ios.goodbit); + assert(v == T(-1)); + } + v = 42; + { + const char str[] = "-"; + std::ios_base::iostate err = ios.goodbit; + input_iterator<const char*> iter = + f.get(input_iterator<const char*>(str), + input_iterator<const char*>(str+sizeof(str)), + ios, err, v); + assert(iter.base() == str+sizeof(str)-1); + assert(err == ios.failbit); + assert(v == 0); + } +} + +template <class T> +void test_negate() { + typedef typename std::make_signed<T>::type SignedT; + const my_facet f(1); + std::ios ios(0); + T v = 42; + { + T value = std::numeric_limits<SignedT>::max(); + ++value; + std::string std_str = make_neg_string(value); + const char* str = std_str.data(); + size_t size = std_str.size(); + std::ios_base::iostate err = ios.goodbit; + input_iterator<const char*> iter = + f.get(input_iterator<const char*>(str), + input_iterator<const char*>(str+size+1), + ios, err, v); + assert(iter.base() == str+size); + assert(err == ios.goodbit); + T expected = -value; + assert(v == expected); + } + v = 42; + { + T value = std::numeric_limits<SignedT>::max(); + ++value; + ++value; + std::string std_str = make_neg_string(value); + const char* str = std_str.data(); + size_t size = std_str.size(); + std::ios_base::iostate err = ios.goodbit; + input_iterator<const char*> iter = + f.get(input_iterator<const char*>(str), + input_iterator<const char*>(str+size+1), + ios, err, v); + assert(iter.base() == str+size); + assert(err == ios.goodbit); + T expected = -value; + assert(v == expected); + } + v = 42; + { + T value = std::numeric_limits<T>::max(); + std::string std_str = make_neg_string(value); + const char* str = std_str.data(); + size_t size = std_str.size(); + std::ios_base::iostate err = ios.goodbit; + input_iterator<const char*> iter = + f.get(input_iterator<const char*>(str), + input_iterator<const char*>(str+size+1), + ios, err, v); + assert(iter.base() == str+size); + assert(err == ios.goodbit); + T expected = -value; + assert(v == expected); + } + v = 42; + { + std::string std_str = make_neg_string(std::numeric_limits<T>::max()); + std_str.back()++; + const char* str = std_str.data(); + size_t size = std_str.size(); + std::ios_base::iostate err = ios.goodbit; + input_iterator<const char*> iter = + f.get(input_iterator<const char*>(str), + input_iterator<const char*>(str+size+1), + ios, err, v); + assert(iter.base() == str+size); + assert(err == ios.failbit); + assert(v == T(-1)); + } +} + +int main(void) +{ + test_neg_one<long>(); + test_neg_one<long long>(); + test_neg_one<unsigned short>(); + test_neg_one<unsigned int>(); + test_neg_one<unsigned long>(); + test_neg_one<unsigned long long>(); + + test_negate<unsigned short>(); + test_negate<unsigned int>(); + test_negate<unsigned long>(); + test_negate<unsigned long long>(); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits