Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-16 Thread Jonathan Wakely

On 15/05/14 22:54 -0400, Ed Smith-Rowland wrote:

On 05/15/2014 03:03 PM, Jonathan Wakely wrote:

Here's a finished patch to simplify bits/parse_numbers.h

Tested x86_64-linux. Ed, any objection to this version?


This looks great, thanks!


I committed that to trunk, I'll put it on the 4.9 branch too.

Having done that should we actually stop using it as suggested in the 
bug trail? ;-)


I was going to do that, then realised that there's a defect in the
standard where it requires overflow in duration integer literals to be
diagnosed. That's only possible with literal operator templates, so I
think we should keep your _Parse_int code, but apply the attached
change to detect overflow.

As the TODO comment says, it should be sufficient to simply
instantiate integral_constant_Rep, _Val::value to give a diagnostic
when _Rep{_Value::value} is narrowing, but GCC only gives a warning
for it, and that's suppressed in a system header, so I do an explicit
static_assert.  That could be replaced with ...

#pragma GCC diagnostic push
#pragma GCC diagnostic error -Woverflow
#pragma GCC diagnostic error -Wsystem-headers
   templatetypename _Dur, char... _Digits
 constexpr _Dur __check_overflow()
 {
   using _Val = __parse_int::_Parse_int_Digits...;
   using _Rep = typename _Dur::rep;
   return _Dur{integral_constant_Rep, _Val::value::value};
 }
#pragma GCC diagnostic pop

... but I have plans to do that sort of thing more widely, which I'll
deal with another time as part of https://gcc.gnu.org/PR50871 and/or
https://gcc.gnu.org/PR58876 (what do other people think about using
diagnostic pragmas to locally re-enable diagnostics in our headers?)

Tested x86_64-linux, committed to trunk.
commit 361c9b79e0b1c7f2435f5b0b369a139b216dee90
Author: Jonathan Wakely jwak...@redhat.com
Date:   Fri May 16 10:31:38 2014 +0100

	* include/bits/parse_numbers.h (__parse_int::_Number_help): Check for
	overflow.
	* include/std/chrono (chrono_literals::__select_type::_Select_type):
	Remove.
	(chrono_literals::_Checked_integral_constant): Define.
	Simplify UDL operator templates and check for overflow.
	* testsuite/20_util/duration/literals/range.cc: New.

diff --git a/libstdc++-v3/include/bits/parse_numbers.h b/libstdc++-v3/include/bits/parse_numbers.h
index 0a42381a..a29d127 100644
--- a/libstdc++-v3/include/bits/parse_numbers.h
+++ b/libstdc++-v3/include/bits/parse_numbers.h
@@ -193,6 +193,7 @@ namespace __parse_int
   _Pow / (_Base * __valid_digit::value),
   _Digs...;
   using type = __ull_constant_Pow * __digit::value + __next::type::value;
+  static_assert((type::value / _Pow) == __digit::value, overflow);
 };
 
   templateunsigned _Base, unsigned long long _Pow, char _Dig
diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
index b114e02..39ad5e3 100644
--- a/libstdc++-v3/include/std/chrono
+++ b/libstdc++-v3/include/std/chrono
@@ -787,117 +787,79 @@ _GLIBCXX_END_NAMESPACE_VERSION
   inline namespace chrono_literals
   {
 
-namespace __select_type
-{
-
-  using namespace __parse_int;
-
-  templateunsigned long long _Val, typename _Dur
-	struct _Select_type
-	: conditional
-	_Val = static_castunsigned long long
-		  (numeric_limitstypename _Dur::rep::max()),
-	_Dur, void
-	{
-	  static constexpr typename _Select_type::type
-	value{static_casttypename _Select_type::type(_Val)};
-	};
-
-  templateunsigned long long _Val, typename _Dur
-	constexpr typename _Select_type_Val, _Dur::type
-	_Select_type_Val, _Dur::value;
+templatetypename _Rep, unsigned long long _Val
+  struct _Checked_integral_constant
+  : integral_constant_Rep, static_cast_Rep(_Val)
+  {
+	static_assert(_Checked_integral_constant::value  0
+		   _Checked_integral_constant::value == _Val,
+		  literal value cannot be represented by duration type);
+  };
 
-} // __select_type
+templatetypename _Dur, char... _Digits
+  constexpr _Dur __check_overflow()
+  {
+	using _Val = __parse_int::_Parse_int_Digits...;
+	using _Rep = typename _Dur::rep;
+	// TODO: should be simply integral_constant_Rep, _Val::value
+	// but GCC doesn't reject narrowing conversions to _Rep.
+	using _CheckedVal = _Checked_integral_constant_Rep, _Val::value;
+	return _Dur{_CheckedVal::value};
+  }
 
 constexpr chrono::durationlong double, ratio3600,1
 operatorh(long double __hours)
 { return chrono::durationlong double, ratio3600,1{__hours}; }
 
 template char... _Digits
-  constexpr typename
-  __select_type::_Select_type__select_int::_Select_int_Digits...::value,
-			 chrono::hours::type
+  constexpr chrono::hours
   operatorh()
-  {
-	return __select_type::_Select_type
-			  __select_int::_Select_int_Digits...::value,
-			  chrono::hours::value;
-  }
+  { return __check_overflowchrono::hours, _Digits...(); }
 
 constexpr chrono::durationlong double, ratio60,1
 

Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-15 Thread Jonathan Wakely

Here's a finished patch to simplify bits/parse_numbers.h

Tested x86_64-linux. Ed, any objection to this version?

commit 87d26af2fc07f0c45a0a6676161ae1db1d7541b7
Author: Jonathan Wakely jwak...@redhat.com
Date:   Wed May 14 16:35:20 2014 +0100

2014-05-15  Ed Smith-Rowland  3dw...@verizon.net
	Jonathan Wakely  jwak...@redhat.com

	PR libstdc++/61166
	* include/bits/parse_numbers.h: Use integral_constant to remove
	duplication and simplify.
	* testsuite/20_util/duration/literals/61166.cc: New.

diff --git a/libstdc++-v3/include/bits/parse_numbers.h b/libstdc++-v3/include/bits/parse_numbers.h
index 91a127c..0a42381a 100644
--- a/libstdc++-v3/include/bits/parse_numbers.h
+++ b/libstdc++-v3/include/bits/parse_numbers.h
@@ -27,8 +27,8 @@
  *  Do not attempt to use it directly. @headername{chrono}
  */
 
-#ifndef _PARSE_NUMBERS_H
-#define _PARSE_NUMBERS_H 1
+#ifndef _GLIBCXX_PARSE_NUMBERS_H
+#define _GLIBCXX_PARSE_NUMBERS_H 1
 
 #pragma GCC system_header
 
@@ -36,289 +36,181 @@
 
 #if __cplusplus  201103L
 
+#include limits
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
-namespace __parse_int {
-
+namespace __parse_int
+{
   templateunsigned _Base, char _Dig
 struct _Digit;
 
   templateunsigned _Base
-struct _Digit_Base, '0'
+struct _Digit_Base, '0' : integral_constantunsigned, 0
 {
-  static constexpr bool valid{true};
-  static constexpr unsigned value{0};
+  using __valid = true_type;
 };
 
   templateunsigned _Base
-struct _Digit_Base, '1'
+struct _Digit_Base, '1' : integral_constantunsigned, 1
 {
-  static constexpr bool valid{true};
-  static constexpr unsigned value{1};
+  using __valid = true_type;
 };
 
-  templateunsigned _Base
-struct _Digit_Base, '2'
+  templateunsigned _Base, unsigned _Val
+struct _Digit_impl : integral_constantunsigned, _Val
 {
-  static_assert(_Base  2, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{2};
+  static_assert(_Base  _Val, invalid digit);
+  using __valid = true_type;
 };
 
   templateunsigned _Base
-struct _Digit_Base, '3'
-{
-  static_assert(_Base  3, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{3};
-};
+struct _Digit_Base, '2' : _Digit_impl_Base, 2
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '4'
-{
-  static_assert(_Base  4, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{4};
-};
+struct _Digit_Base, '3' : _Digit_impl_Base, 3
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '5'
-{
-  static_assert(_Base  5, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{5};
-};
+struct _Digit_Base, '4' : _Digit_impl_Base, 4
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '6'
-{
-  static_assert(_Base  6, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{6};
-};
+struct _Digit_Base, '5' : _Digit_impl_Base, 5
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '7'
-{
-  static_assert(_Base  7, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{7};
-};
+struct _Digit_Base, '6' : _Digit_impl_Base, 6
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '8'
-{
-  static_assert(_Base  8, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{8};
-};
+struct _Digit_Base, '7' : _Digit_impl_Base, 7
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '9'
-{
-  static_assert(_Base  9, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{9};
-};
+struct _Digit_Base, '8' : _Digit_impl_Base, 8
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, 'a'
-{
-  static_assert(_Base  0xa, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{0xa};
-};
+struct _Digit_Base, '9' : _Digit_impl_Base, 9
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, 'A'
-{
-  static_assert(_Base  0xa, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{0xa};
-};
+struct _Digit_Base, 'a' : _Digit_impl_Base, 0xa
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, 'b'
-{
-  static_assert(_Base  0xb, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{0xb};
-};
+struct _Digit_Base, 'A' : _Digit_impl_Base, 0xa
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, 'B'
-{
-  static_assert(_Base  0xb, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr 

Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-15 Thread Ed Smith-Rowland

On 05/15/2014 03:03 PM, Jonathan Wakely wrote:

Here's a finished patch to simplify bits/parse_numbers.h

Tested x86_64-linux. Ed, any objection to this version?


This looks great, thanks!

Having done that should we actually stop using it as suggested in the 
bug trail? ;-)




[PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-14 Thread Ed Smith-Rowland

Make the machinery in bits/parse_number.h unsigned long long.
I had actually noticed this a while back but we were in stage 4. Then I 
forgot.. :-/


Built and tested on x84_64-linux.

OK?


2014-05-14  Ed Smith-Rowland  3dw...@verizon.net

libstdc++/61166 overflow when parse number in std::duration operator
* include/bits/parse_numbers.h: Make the machinery unsigned long long.
* testsuite/20_util/duration/literals/pr61166.cc: New.
Index: include/bits/parse_numbers.h
===
--- include/bits/parse_numbers.h(revision 210315)
+++ include/bits/parse_numbers.h(working copy)
@@ -27,8 +27,8 @@
  *  Do not attempt to use it directly. @headername{chrono}
  */
 
-#ifndef _PARSE_NUMBERS_H
-#define _PARSE_NUMBERS_H 1
+#ifndef _GLIBCXX_PARSE_NUMBERS_H
+#define _GLIBCXX_PARSE_NUMBERS_H 1
 
 #pragma GCC system_header
 
@@ -36,262 +36,278 @@
 
 #if __cplusplus  201103L
 
+#include limits
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
-namespace __parse_int {
+namespace __parse_int
+{
 
-  templateunsigned _Base, char _Dig
+  templateunsigned long long _Base, char _Dig
 struct _Digit;
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '0'
 {
   static constexpr bool valid{true};
-  static constexpr unsigned value{0};
+  static constexpr unsigned long long value{0};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '1'
 {
   static constexpr bool valid{true};
-  static constexpr unsigned value{1};
+  static constexpr unsigned long long value{1};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '2'
 {
   static_assert(_Base  2, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{2};
+  static constexpr unsigned long long value{2};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '3'
 {
   static_assert(_Base  3, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{3};
+  static constexpr unsigned long long value{3};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '4'
 {
   static_assert(_Base  4, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{4};
+  static constexpr unsigned long long value{4};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '5'
 {
   static_assert(_Base  5, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{5};
+  static constexpr unsigned long long value{5};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '6'
 {
   static_assert(_Base  6, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{6};
+  static constexpr unsigned long long value{6};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '7'
 {
   static_assert(_Base  7, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{7};
+  static constexpr unsigned long long value{7};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '8'
 {
   static_assert(_Base  8, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{8};
+  static constexpr unsigned long long value{8};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, '9'
 {
   static_assert(_Base  9, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{9};
+  static constexpr unsigned long long value{9};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, 'a'
 {
   static_assert(_Base  0xa, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{0xa};
+  static constexpr unsigned long long value{0xa};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, 'A'
 {
   static_assert(_Base  0xa, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{0xa};
+  static constexpr unsigned long long value{0xa};
 };
 
-  templateunsigned _Base
+  templateunsigned long long _Base
 struct _Digit_Base, 'b'
 {
   static_assert(_Base  0xb, invalid digit);
   static constexpr bool valid{true};
-  static constexpr unsigned value{0xb};
+  static constexpr unsigned long long value{0xb};
 };
 
-  templateunsigned _Base
+  

Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-14 Thread Daniel Krügler
2014-05-14 15:38 GMT+02:00 Ed Smith-Rowland 3dw...@verizon.net:
 Make the machinery in bits/parse_number.h unsigned long long.
 I had actually noticed this a while back but we were in stage 4. Then I
 forgot.. :-/

 Built and tested on x84_64-linux.

 OK?

I understand the reason why the corresponding static members value got
type unsigned long long, but why did the template parameter _Base also
require the same update?

Another question: Presumably value indded requires no uglification,
but what about the member valid? I would expect that this is no name
reserved by the library.

- Daniel



-- 


SavedURI :Show URLShow URLSavedURI :
SavedURI :Hide URLHide URLSavedURI :
https://mail.google.com/_/scs/mail-static/_/js/k=gmail.main.de.LEt2fN4ilLE.O/m=m_i,t,it/am=OCMOBiHj9kJxhnelj6j997_NLil29vVAOBGeBBRgJwD-m_0_8B_AD-qOEw/rt=h/d=1/rs=AItRSTODy9wv1JKZMABIG3Ak8ViC4kuOWA?random=1395770800154https://mail.google.com/_/scs/mail-static/_/js/k=gmail.main.de.LEt2fN4ilLE.O/m=m_i,t,it/am=OCMOBiHj9kJxhnelj6j997_NLil29vVAOBGeBBRgJwD-m_0_8B_AD-qOEw/rt=h/d=1/rs=AItRSTODy9wv1JKZMABIG3Ak8ViC4kuOWA?random=1395770800154



Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-14 Thread Jonathan Wakely
On 14 May 2014 14:59, Daniel Krügler wrote:
 2014-05-14 15:38 GMT+02:00 Ed Smith-Rowland 3dw...@verizon.net:
 Make the machinery in bits/parse_number.h unsigned long long.
 I had actually noticed this a while back but we were in stage 4. Then I
 forgot.. :-/

 Built and tested on x84_64-linux.

 OK?

 I understand the reason why the corresponding static members value got
 type unsigned long long, but why did the template parameter _Base also
 require the same update?

 Another question: Presumably value indded requires no uglification,
 but what about the member valid? I would expect that this is no name
 reserved by the library.

Good point. (It's a member function in at least future, but we
shouldn't be reserving it elsewhere).

Any time I see a static const member of integral type called value I
wonder if the type should derive from a specialization of
std::integral_constant. You could probably also do:

using __valid = true_type;


Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-14 Thread Ed Smith-Rowland

On 05/14/2014 09:59 AM, Daniel Krügler wrote:

2014-05-14 15:38 GMT+02:00 Ed Smith-Rowland 3dw...@verizon.net:

Make the machinery in bits/parse_number.h unsigned long long.
I had actually noticed this a while back but we were in stage 4. Then I
forgot.. :-/

Built and tested on x84_64-linux.

OK?

I understand the reason why the corresponding static members value got
type unsigned long long, but why did the template parameter _Base also
require the same update?

Another question: Presumably value indded requires no uglification,
but what about the member valid? I would expect that this is no name
reserved by the library.

- Daniel



You're right, _Base doesn't need that - only 2, 8, 10, 16 allowed (could 
do base64 someday, but still).  Change all got me.


I should uglify the valid members.
But in keeping with, say, our extension type traits and such maybe i 
should uglify value as well.




Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-14 Thread Jonathan Wakely
On 14 May 2014 15:25, Ed Smith-Rowland wrote:
 But in keeping with, say, our extension type traits and such maybe i should
 uglify value as well.

No, just derive from std::integral_constant and you get value for free.

You already use integral_constant in that file, so the name value is
already used.


Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-14 Thread Jonathan Wakely
On 14 May 2014 15:36, Jonathan Wakely wrote:
 On 14 May 2014 15:25, Ed Smith-Rowland wrote:
 But in keeping with, say, our extension type traits and such maybe i should
 uglify value as well.

 No, just derive from std::integral_constant and you get value for free.

 You already use integral_constant in that file, so the name value is
 already used.

That also has the advantage that _DigitB, 'f' and _DigitB, 'F'
share the same base class, so you don't end up with two different
static members with the same value (and if you make __valid a typedef
as I suggested you don't have any static members for that either).

Do we really need _Digit::value to be unsigned long long, or is it
only the results in _Power_help and _Number_help that need to be
64-bit?


Re: [PATCH, libstdc++/61166] overflow when parse number in std::duration operator

2014-05-14 Thread Jonathan Wakely

On 14/05/14 15:41 +0100, Jonathan Wakely wrote:

On 14 May 2014 15:36, Jonathan Wakely wrote:

On 14 May 2014 15:25, Ed Smith-Rowland wrote:

But in keeping with, say, our extension type traits and such maybe i should
uglify value as well.


No, just derive from std::integral_constant and you get value for free.

You already use integral_constant in that file, so the name value is
already used.


That also has the advantage that _DigitB, 'f' and _DigitB, 'F'
share the same base class, so you don't end up with two different
static members with the same value (and if you make __valid a typedef
as I suggested you don't have any static members for that either).


I've attached a first pass at doing it that way, using
integral_constant base classes and working with types not values as
much as possible. This gets rid of all static const members except in
_Digits (where is that used?)


Do we really need _Digit::value to be unsigned long long, or is it
only the results in _Power_help and _Number_help that need to be
64-bit?


Because I made _Digit derive from integral_constantunsigned that
means the partial specialization of _Number_help for single digits has
_Number_help::type as integral_constantunsigned, where the other
specializations have integral_constantunsigned long long. That's
inconsistent, but works. Maybe we'd want to be consistent though, and
use this instead of typename _DigitBase, _Dig::type:

 using type = integral_constantunsigned long long,
_Digit_Base, _Dig::value;

(Also, I wonder if we could use std::ratio for the power/base
arithmetic, but it might not help.)
diff --git a/libstdc++-v3/include/bits/parse_numbers.h b/libstdc++-v3/include/bits/parse_numbers.h
index 91a127c..055058e 100644
--- a/libstdc++-v3/include/bits/parse_numbers.h
+++ b/libstdc++-v3/include/bits/parse_numbers.h
@@ -46,185 +46,109 @@ namespace __parse_int {
 struct _Digit;
 
   templateunsigned _Base
-struct _Digit_Base, '0'
+struct _Digit_Base, '0' : integral_constantunsigned, 0
 {
-  static constexpr bool valid{true};
-  static constexpr unsigned value{0};
+  using __valid = true_type;
 };
 
   templateunsigned _Base
-struct _Digit_Base, '1'
+struct _Digit_Base, '1' : integral_constantunsigned, 1
 {
-  static constexpr bool valid{true};
-  static constexpr unsigned value{1};
+  using __valid = true_type;
 };
 
-  templateunsigned _Base
-struct _Digit_Base, '2'
+  templateunsigned _Base, unsigned _Val
+struct _Digit_impl : integral_constantunsigned, _Val
 {
-  static_assert(_Base  2, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{2};
+  static_assert(_Base  _Val, invalid digit);
+  using __valid = true_type;
 };
 
   templateunsigned _Base
-struct _Digit_Base, '3'
-{
-  static_assert(_Base  3, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{3};
-};
+struct _Digit_Base, '2' : _Digit_impl_Base, 2
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '4'
-{
-  static_assert(_Base  4, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{4};
-};
+struct _Digit_Base, '3' : _Digit_impl_Base, 3
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '5'
-{
-  static_assert(_Base  5, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{5};
-};
+struct _Digit_Base, '4' : _Digit_impl_Base, 4
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '6'
-{
-  static_assert(_Base  6, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{6};
-};
+struct _Digit_Base, '5' : _Digit_impl_Base, 5
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '7'
-{
-  static_assert(_Base  7, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{7};
-};
+struct _Digit_Base, '6' : _Digit_impl_Base, 6
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '8'
-{
-  static_assert(_Base  8, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{8};
-};
+struct _Digit_Base, '7' : _Digit_impl_Base, 7
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, '9'
-{
-  static_assert(_Base  9, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{9};
-};
+struct _Digit_Base, '8' : _Digit_impl_Base, 8
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, 'a'
-{
-  static_assert(_Base  0xa, invalid digit);
-  static constexpr bool valid{true};
-  static constexpr unsigned value{0xa};
-};
+struct _Digit_Base, '9' : _Digit_impl_Base, 9
+{ };
 
   templateunsigned _Base
-struct _Digit_Base, 'A'
-