lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] Fixing build with clang 7 and 64-bit Linux


From: Greg Chicares
Subject: Re: [lmi] Fixing build with clang 7 and 64-bit Linux
Date: Tue, 13 Nov 2018 17:21:02 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.0

On 2018-11-13 15:01, Vadim Zeitlin wrote:
> On Tue, 13 Nov 2018 09:57:48 +0000 Greg Chicares <address@hidden> wrote:
> 
> GC> We seem to be looking at this in completely different ways, apparently
> GC> because we have different answers to a particular question pertaining
> GC> to Dietmar Kuehl's original C98 code: given
> GC>   static std::ctype_base::mask rc[table_size];
> GC>   rc[C] &= ~std::ctype_base::space;
> GC> what is the type of the expression
> GC>            ~std::ctype_base::space
> GC> on the RHS of the assignment?
> 
>  I think it's implementation-defined.

I'm coming around to the position that the standard is defectively unclear;
i.e., that N3110
  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3110.html
was correct in saying:

| Bitmask types need operator~ in order to clear a value from the bitmask
| type, as show in 17.5.2.1.3 paragraph 4:
|
| — To clear a value Y in an object X is to evaluate the expression X &= ~Y.
|
| However the definition for fmtflags above does not have well-specified
| behaviour if the underlying type is smaller than int, because ~int(f) is
| likely to produce a value outside the range of fmtflags.
|
| The same problem is present in the example implementation in 17.5.2.1.3
| because int_type is only stated to be capable of holding all the values of
| bitmask so there is no guarantee that sizeof(int_type) == sizeof(bitmask)
| and therefore no guarantee that the code given for operator~ works as
| intended.

yet didn't go far enough to fix this problem.

> GC> I believe that expression's type must be 'ctype_base::mask':
> GC> 
> GC>  - 'ctype_base::mask' is a "bitmask" type (C++17 [locale.types]);
> 
>  And [bitmask.types] says
> 
>       Each bitmask type can be implemented as an enumerated type that
>       overloads certain operators, as an integer type, or as a bitset
>                                    ^^^^^^^^^^^^^^^^^^
> 
> (underlining mine). I'm reading this as saying that implementing
> ctype_base::mask as an integer type is explicitly allowed. Am I wrong?

Clearly a bitmask type may be implemented as an integer type. But not
every integer type is allowed. Certainly type 'bool' isn't allowed for
bitmask types that require more than one bit. By the logic of N3110
(above), I would argue that any integer type that isn't closed under '~'
isn't clearly allowed: if it were, then [bitmask.types/4.2]:
| To clear a value Y in an object X is to evaluate the expression X &= ~Y.
is confusing at best. A standard that says "the way to do A is B" is
dubious when doing B appropriately elicits a warning.

> GC>  - the implementation
> GC>       constexpr bitmask operator~(bitmask X){
> GC>       return static_cast<bitmask>(~static_cast<int_type>(X));
> GC>       }
> GC>    [bitmask.types/2] is normative.
> 
>  Is it? This snippet starts with "// For exposition only." and, anyhow,
> "can be written" doesn't imply "must be written" to me.

In that section:

// For exposition only.
// int_type is an integral type capable of representing all values of the 
bitmask type.
enum bitmask : int_type {
V 0 = 1 << 0, V 1 = 1 << 1, V 2 = 1 << 2, V 3 = 1 << 3, .....

I had taken "For exposition only" as applying only to the single
comment line that follows it. But I was mistaken there: if
  enum bitmask : int_type {
were a normative requirement, then an integer type or a bitfield
would not be allowed.

> Also, quoting
> another part of your email occurring later:
> 
> GC> It would appear that, instead of the normative
> GC> 
> GC>       constexpr bitmask operator~(bitmask X){
> GC>       return static_cast<bitmask>(~static_cast<int_type>(X));
> GC>       }
> 
>  Certainly this can't be normative if bitmask is defined as an integer type
> because an overloaded operator must take an argument of class or enum type,
> i.e. writing this when bitmap is a typedef for "[unsigned] short" is a
> compilation error.

Yes, I found that out when, conjecturing that gcc might merely have
omitted to define such an operator~(), I tried to add it--only to be
told that I can't do that if 'bitmask' is a builtin type.

> GC> Moreover, the language "To clear a value Y in an object X is to
> GC> evaluate the expression X &= ~Y" in [locale.types/2] is certainly
> GC> normative, so issuing a warning on 'X &= ~Y' is extraordinary.
> 
>  I agree that it seems pretty strange, although it's probably a QoI issue
> and not a conformance one. Just to be sure, note that it's gcc which does
> this, while clang doesn't.

Wow--then perhaps clang treats 'X &= ~Y' for bitmask types as a special
case, distinguishing a "bitmask unsigned short int" type from the usual
"unsigned short int", and suppressing the out-of-range warning in the
former case only, so as to give the bitmask 'X &= ~Y' operation its
intended meaning.

>  I don't think it's prescribed. I do think the standard text is confusing
> here, but I'm almost sure the intention was to allow using integer types
> as bitmask types.

In that case, this N3110 observation

| However the definition for fmtflags above does not have well-specified
| behaviour if the underlying type is smaller than int, because ~int(f) is
| likely to produce a value outside the range of fmtflags.

remains valid, and one possible correction would be:

- To clear a value Y in an object X is to evaluate the expression X &= ~Y.
+ To clear a value Y in an object X is to evaluate the expression
+ static_cast<bitmask>(~static_cast<int_type>(X)).

I'm pushing a change that does something like that. It works with gcc-7.3;
please let me know if it doesn't avoid clang warnings.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]