Re: [PATCH] Add cast to unsigned char to strverscmp

Christian Franke <[email protected]>
Newsgroups gmane.comp.lib.newlib
Message-ID <[email protected]>
Corinna Vinschen wrote:
> On Nov  8 15:37, Corinna Vinschen wrote:
>> On Nov  8 12:41, Christian Franke wrote:
>>> Corinna Vinschen wrote:
>>>> On Nov  7 13:58, Joel Sherrill wrote:
>>>>> On Thu, Nov 7, 2024 at 1:06 PM <[email protected]> wrote:
>>>>>
>>>>>> On 2024-11-07 09:53, Jeremy Bettis wrote:
>>>>>>> GCC 14.2 with -Werror=sign-compare fails on this code.
>>>>>>>
>>>>>>> Signed-off-by: Jeremy Bettis <[email protected] <mailto:
>>>>>> [email protected]>>
>>>>>>> ---
>>>>>>>     newlib/libc/string/strverscmp.c | 2 +-
>>>>>>>     1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>>>
>>>>>>> diff --git a/newlib/libc/string/strverscmp.c
>>>>>> b/newlib/libc/string/strverscmp.c
>>>>>>> index 55966335f..e86718faa 100644
>>>>>>> --- a/newlib/libc/string/strverscmp.c
>>>>>>> +++ b/newlib/libc/string/strverscmp.c
>>>>>>> @@ -76,7 +76,7 @@ int strverscmp(const char *l0, const char *r0)
>>>>>>>      else if (c!='0') z=0;
>>>>>>>      }
>>>>>>>
>>>>>>> - if (l[dp]<'1' + 9U && r[dp]<'1' + 9U) {
>>>> I'm puzzled.  This doesn't look like newlib code.  Newlib's code looks
>>>> like this:
>>>>
>>>>           if (l[dp]-'1'<9U && r[dp]-'1'<9U) {
>>>>
>>>> See https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=newlib/libc/string/strverscmp.c;hb=HEAD#l79
>>>>
>>>> Given that l and r are unsigned char anyway, the entire expression
>>>> is unsigned, so there shouldn't be a sign-compare error in newlib's
>>>> version.
>>>>
>>> It's actually signed, IIRC due to changing 'unsigned preserving' (K&R C) to
>>> 'value preserving' (C89) implicit conversions.
>>>
>>> "Proof" using C++17 compile time checks:
>>>
>>> $ cat uchar2int.cc
>>> #include <cstddef>
>>> #include <type_traits>
>>>
>>> const unsigned char *l;
>>> std::size_t dp;
>>>
>>> static_assert(std::is_same_v<decltype(l[dp]), const unsigned char &>);
>>> static_assert(std::is_same_v<decltype(l[dp]-'1'), int>);
>>> static_assert(std::is_same_v<decltype(l[dp]-0x31U), unsigned int>);
>>>
>>> $ g++ -S -Wall -W uchar2int.cc
>>> [no failed assert]
>> Drat.  Looks like I'm still living in K&R country...
> So then, what about
>
>    if (l[dp]-U'1'<9U && r[dp]-U'1'<9U) {
>
> At least
>
>    static_assert(std::is_same_v<decltype(l[dp]-U'1'), unsigned int>);
>
> is happy.
>

In practice, this should work for all existing use cases of newlib. In 
theory, it is not generally portable:

U'1' has type char32_t (C11) which is unsigned. If the very rare ILP64 
data model would be used, this would be promoted to 64-bit signed int.

This variant does not depend on signedness or integer overflow:

   if ('1' <= l[dp] && l[dp] <= '9' && '1' <= r[dp] && r[dp] <= '9') {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.