On Tue, 27 Mar 2012 19:20:52 -0700, Russ Allbery wrote:

> (For example, b++ could easily wrap, and unexpectedly fast
> depending on the size of bool on a platform.)

Actually, it would appear that a bool (or a _Bool) can't wrap
on increment, but it CAN wrap on decrement (and strangely,
when the operand is a bool, C++ not only deprecates the prefix
and postfix `++' operators, but also forbids the prefix and
postfix `--' operators; this is particularly strange given
that similar semantics are still to be had with the `+='
and `-=' compound assignments).  

I'm going to deal with the prefix operators rather than the
postfix operators, in order to concentrate on the value that
is newly assigned to the variable in question.

Firstly, the C++ standard is very explicit and concise.
According to C++11.5.3.2 "Increment and decrement":

  1 The operand of prefix ++ is modified by adding 1, or set to
    true if it is bool (this use is deprecated). The operand
    shall be a modifiable lvalue. The type of the operand shall
    be an arithmetic type or a pointer to a completely-defined
    object type. The result is the updated operand; it is an
    lvalue, and it is a bit-field if the operand is a bit-field.
    If x is not of type bool, the expression ++x is equivalent
    to x+=1...

  2 The operand of prefix -- is modified by subtracting 1. The
    operand shall not be of type bool. The requirements on the
    operand of prefix -- and the properties of its result are
    otherwise the same as those of prefix ++...

As for C99, indulge me for a moment.

According to C99.6.5.3.1 "Prefix increment and decrement operators":

    ...

  2 The value of the operand of the prefix ++ operator is
    incremented. The result is the new value of the operand after
    incrementation. The expression ++E is equivalent to (E+=1).
    See the discussions of additive operators and compound
    assignment for information on constraints, types, side
    effects, and conversions and the effects of operations on
    pointers.

    ...

and according to C99.6.5.16.2 "Compound Assignment":

    ...

  3 A compound assignment of the form E1 op= E2 differs from the
    simple assignment expression E1 = E1 op (E2) only in that the
    lvalue E1 is evaluated only once.


and according to C99.6.5.6 "Additive operators":

    ...

  2 For addition, either both operands shall have arithmetic
    type, or one operand shall be a pointer to an object type
    and the other shall have integer type. (Incrementing is
    equivalent to adding 1.)

    ...

  4 If both operands have arithmetic type, the usual arithmetic
    conversions are performed on them.

  5 The result of the binary + operator is the sum of the
    operands.

    ...

and according to C99.6.2.5 "Types":

    ...

  6 ... The type _Bool and the unsigned integer types that correspond
    to the standard signed integer types are the standard unsigned
    integer types... The standard and extended unsigned integer types
    are collectively called unsigned integer types.

    ...

 17 The type char, the signed and unsigned integer types, and the
    enumerated types are collectively called integer types...

 18 Integer and floating types are collectively called arithmetic
    types.


    ...

and according to C99.6.3.1.8 "Usual arithmetic conversions":

  1 ...
    
        Otherwise, the integer promotions are performed on both
        operands. Then the following rules are applied to the
        promoted operands:
    
            If both operands have the same type, then no further
            conversion is needed.

            ...
        ...
    ...

and according to C99.6.3.1.1 "Boolean, characters, and integers"

 2  ... If an int can represent all values of the original
    type, the value is converted to an int; otherwise, it is
    converted to an unsigned int. These are called the integer
    promotions...

and according to C99.6.3.1.2 "Boolean type":

  1 When any scalar value is converted to _Bool,
    the result is 0 if the value compares equal
    to 0; otherwise, the result is 1.

and according to C99.7.16 "Boolean type and values <stdbool.h>":

  1 The header <stdbool.h> defines four macros.

  2 The macro

        bool

    expands to _Bool.

    ...

So, the prefix `++' operator expression in the following:

  bool b = 1;
  ++b;

should assign to `b' the value of the following expression:

  (bool)((int)b + (int)1)
  (bool)((int)1 + (int)1)
  (bool)2
  1

That is, the value of `b' should remain `1', which is corroborated
when the following program:

  #include <stdbool.h>
  int main(void)
  {
    bool b = 1;
    for (;;) ++b;
  }

is compiled with:

  gcc -std=c99 -pedantic -Wall -O0 -fdump-tree-gimple d.c

thereby yield the following GIMPLE representation:

  main ()
  {
    int D.1090;

    {
      _Bool b;

      b = 1;
      <D.1088>:
      b = 1;
      goto <D.1088>;
    }
    D.1090 = 0;
    return D.1090;
  }

As you can see, the loop simply keeps assigning to `b' the value `1':

      <D.1088>:
      b = 1;
      goto <D.1088>;

As for decrementing a _Bool, according to C99.6.5.3.1
"Prefix increment and decrement operators":

    The prefix -- operator is analogous to the prefix ++
    operator, except that the value of the operand is
    decremented.

So, the prefix `--' operator expression in the following:

  #include <stdbool.h>
  bool b = 0;
  --b;

should assign to `b' the value of the following expression:

  (bool)((int)b - (int)1)
  (bool)((int)0 - (int)1)
  (bool)-1
  1

That is, the value of `b' can wrap on decrement, which is
corroborated when the following program:

  #include <stdbool.h>
  int main(void)
  {
    bool b = 0;
    for (;;) --b;
  }

is compiled with:

  gcc -std=c99 -pedantic -Wall -O0 -fdump-tree-gimple d.c

thereby yield the following GIMPLE representation:

  main ()
  {
    int D.1090;
  
    {
      _Bool b;
  
      b = 0;
      <D.1088>:
      b = !b;
      goto <D.1088>;
    }
    D.1090 = 0;
    return D.1090;
  }

As you can see, the loop simply keeps assigning to `b' the logical
negation of `b':

      <D.1088>:
      b = !b;
      goto <D.1088>;

Similar arguments can be made for `b+=1' and `b-=1' (even for C++);
however, the GIMPLE representation is sometimes not as optimized:
For `b+=1' (and similarly for `b-=1'), gcc produces the following
more general computation:

      <D.1088>:
      D.1090 = (int) b;
      D.1091 = D.1090 + 1;
      b = D.1091 != 0;
      goto <D.1088>;

Also, while g++ does the simple logical negation for `b-=1', it
instead forgos a simple assignment of `1' in favor of the following
bizarre gymnastics for `b+=1':

      <D.983>:
      D.984 = (int) b;
      b = D.984 != -1;
      goto <D.983>;

Maybe there's room for a patch?

Anyway, I'm done with my mental pleasuring for the day.

Ta ta!
Michael Witten

Reply via email to