signed/unsigned integer conversion for right shift seems against C99 rule

Peter Breuer peter.t.breuer@gmail.com
Tue Feb 6 15:03:00 GMT 2018

```int main() {
signed   int x = 0x80000005u;
unsigned int y = 0x00000002u;
signed   int z = x >> y;
printf("0x%0x\n", z);
return 0;
}
% gcc -std=c99 test.c
test.c: In function 'main':
test.c:6:3: warning: implicit declaration of function 'printf' [-Wimplicit-function-declaration]
printf("0x%0x\n", z);
^
test.c:6:3: warning: incompatible implicit declaration of built-in function 'printf'

(I'll live with that warning - just for the purpose of a clean example!)

% ./a.out
0xe0000001
^ SIGNED right shift

OK, so x>>y was done signed. That means both x,y were converted to
signed int (trivially).

However, that seems against C99 rules, which seem to say one should
convert to unsigned int in mixed sign equal rank situations.

First: NO INTEGER PROMOTION is done, by this rule

any operand whose type ranks lower than int is automatically converted
to the type int, provided int is capable of representing all values of
the operand's original type.  If int is not sufficient, the operand is
converted to unsigned int.

Both x and y are int, so do not rank "lower than int". Rule does not
apply. All as is. So INTEGER CONVERSION applies from there:

If both operands are integers, integer promotion is first performed on
both operands.  If after integer promotion the operands still have
different types, conversion continues as follows:

They still have "different types", one being signed int and the other
being unsigned it. So CONVERSION happens:

If one operand has an unsigned type T whose conversion rank is at
least as high as that of the other operand's type, then the other
operand is converted to type T.

We're there. Both are rank "int" so "at least as high" applies. One
(y) has an unsigned type T="unsigned int", so the other (x) must
be converted to T="unsigned int". Then the operation gets done as

unsigned int >> unsigned int

Except it doesn't. The sign bit on the LHS was extended as the printf
showed with its "0xe..." at the front, so a shift-right-arithmetic got
done, not a shift-right-logical.

If you wonder if a SRL is available at all, it is.  Declaring both x and
y as unsigned int (i.e., change decl of x only to unsigned int) gives
the expected SRL output.

% ./a.out
0x20000001
^ zero-extended from 0x8..., not sign-extended. That's a SRL, not a SRA.

What am I reading wrong in the standards?  (which are nigh on
incomprehensible - what does "can be represented as" mean?  One can't
represent negative numbers in the unsigned version of the same type
ever, and one can't represent large positive numbers in the signed
version of the type ever, so one can never get "can be represented as").

Is this intended? Well-known to everyone but me?

% gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/i586-linux-gnu/4.9/lto-wrapper
Target: i586-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10'
--enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr
--libexecdir=/usr/lib --without-included-gettext
--libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu
--enable-libstdcxx-debug --enable-libstdcxx-time=yes
--enable-gnu-unique-object --disable-vtable-verify --enable-plugin
--with-system-zlib --disable-browser-plugin --enable-java-awt=gtk
--enable-gtk-cairo
--with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-i386/jre
--enable-java-home
--with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-i386
--with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-i386
--with-arch-directory=i386
--with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc
--enable-targets=all --enable-multiarch --with-arch-32=i586
--with-multilib-list=m32,m64,mx32 --enable-multilib
--with-tune=generic --enable-checking=release --build=i586-linux-gnu
--host=i586-linux-gnu --target=i586-linux-gnu Thread model: posix gcc
version 4.9.2 (Debian 4.9.2-10)

The gcc notes at https://gcc.gnu.org/c99status.html seem to say

integer constant type rules   GCC 3.3
integer promotion rules GCC 4.0

and nothing else.  Where is current information on standards compliance?
The manual  says

c99
c9x
iso9899:1999
iso9899:199x
ISO C99.  Note that this standard is not yet fully supported;
^^^^^^^^^^^^^^^^^^^^^^^^
see <http://gcc.gnu.org/gcc-4.7/c99status.html> for more
information.  The names c9x and iso9899:199x are deprecated.

Any info that would deconfuse me?

Regards to any language lawyers who pass by on the other side or this side.

PTB

```