Bug 16693

Summary: [4.0 regression] Bitwise AND is lost when used within a cast to an enum of the same precision
Product: gcc Reporter: Paul Gotch <p.r.gotch>
Component: middle-endAssignee: roger
Severity: critical CC: gcc-bugs, mmitchel, reichelt, roger
Priority: P2 Keywords: monitored, wrong-code
Version: 3.4.1   
Target Milestone: 3.4.2   
Host: i686-pc-linux-gnu Target:
Build: i686-pc-linux-gnu Known to work: 3.3.3 3.3.4 3.4.2 4.0.0
Known to fail: 3.4.0 3.4.1 Last reconfirmed: 2004-07-23 19:04:45
Attachments: Preprocessed output of the code in the defect report

Description Paul Gotch 2004-07-23 17:15:25 UTC
The following code produces incorrect results when compiled with GCC 3.4.0 and
3.4.1. With both of these compilers the bitwise "AND" gets ignored and the value
of "r1" ends up as 0x66 rather than 0x6. GCC 3.3.3 generates the correct answer
as does GCC 3.5.0 snapshot taken on 20040717.

The console output of the compiler during this run was:

Reading specs from /arm/eda/tools/gnu/gcc/3_4_1/linux_rh_7_2-x86/lib/gcc/i686-pc
Configured with: ./configure --prefix=/arm/eda/tools/gnu/gcc/3_4_1 --exec-prefix
=/arm/eda/tools/gnu/gcc/3_4_1/linux_rh_7_2-x86 --program-suffix=-3_4_1 -v --with
-dwarf2 --enable-version-specific-runtime-libs --with-gnu-as --with-as=/arm/eda/
tools/gnu/binutils/2_15_90_0_3/linux_rh_7_2-x86/bin/as-2_15_90_0_3 --with-gnu-ld
Thread model: posix
gcc version 3.4.1
.1/cc1plus -E -quiet -v -D_GNU_SOURCE tst_gcc.cpp -mtune=pentiumpro -o tst_gcc.i
ignoring nonexistent directory "/arm/eda/tools/gnu/gcc/3_4_1/linux_rh_7_2-x86/li
#include "..." search starts here:
#include <...> search starts here:
End of search list.
.1/cc1plus -fpreprocessed tst_gcc.ii -quiet -dumpbase tst_gcc.cpp -mtune=pentium
pro -auxbase tst_gcc -version -o tst_gcc.s
GNU C++ version 3.4.1 (i686-pc-linux-gnu)
        compiled by GNU C version 3.4.1.
GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=129190
 /arm/eda/tools/gnu/binutils/2_15_90_0_3/linux_rh_7_2-x86/bin/as-2_15_90_0_3 -V 
-Qy -o tst_gcc.o tst_gcc.s
GNU assembler version (i686-pc-linux-gnu) using BFD version 2.15.90.
0.3 20040415
.1/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o tst
_gcc /usr/lib/crt1.o /usr/lib/crti.o /arm/eda/tools/gnu/gcc/3_4_1/linux_rh_7_2-x
86/lib/gcc/i686-pc-linux-gnu/3.4.1/crtbegin.o -L/arm/eda/tools/gnu/gcc/3_4_1/lin
ux_rh_7_2-x86/lib/gcc/i686-pc-linux-gnu/3.4.1 -L/arm/eda/tools/gnu/gcc/3_4_1/lin
ux_rh_7_2-x86/lib/gcc/i686-pc-linux-gnu/3.4.1/../../.. tst_gcc.o -lstdc++ -lm -l
gcc_s -lgcc -lc -lgcc_s -lgcc /arm/eda/tools/gnu/gcc/3_4_1/linux_rh_7_2-x86/lib/
gcc/i686-pc-linux-gnu/3.4.1/crtend.o /usr/lib/crtn.o

#include <iostream>

unsigned short ret6666(int) {
    return 0x66;

typedef enum {
    a   = 0x0, b   = 0x1, c   = 0x2, d   = 0x3, e   = 0x4, f   = 0x5,
    g   = 0x6, h   = 0x7, i   = 0x8, j   = 0x9, k   = 0xa, l   = 0xb,
    m   = 0xc, n   = 0xd, o   = 0xe, p   = 0xf 
} Test_Enum;

int main(void) {
    unsigned char r1;
    r1 = static_cast<Test_Enum>(0xf & ret6666(44));

    if(r1 == 0x6) {
        std::cout << "Passed" << std::endl;
    } else {
        std::cout << "Failed" << std::endl;
Comment 1 Paul Gotch 2004-07-23 17:18:37 UTC
Created attachment 6814 [details]
Preprocessed output of the code in the defect report
Comment 2 Wolfgang Bangerth 2004-07-23 19:04:45 UTC
Confirmed. A regression in 3.4 and mainline against 3.3.4. 
Comment 3 Wolfgang Bangerth 2004-07-23 19:06:13 UTC
BTW, my mainline snapshot from 2004-07-21 fails this testcase, too. 
Comment 4 Paul Gotch 2004-07-24 00:10:26 UTC
I've now tried this on SPARC as well as i686 and I get the same results which
suggests this is a front end rather than a target specific problem.
Comment 5 Paul Gotch 2004-07-26 13:02:59 UTC
A collegue has done some more investigation and come up with the following
additional constraints.

If the enum pushes into the top nibble then the problem does not occur.

A similar problem occurs with unsigned short where the problem occurs if the
enumeration fits into a byte.

If the target variable is an unsigned int rather than a unsigned char or short
the problem does not occur.
Comment 6 Andrew Pinski 2004-07-26 13:50:22 UTC
I think this is caused by fold which does the following transformation:
(cast)(a & constant) -> ((cast)a) & ((cast)constant).
Comment 7 Volker Reichelt 2004-08-10 09:05:11 UTC
Here's a condensed version (which should return 0):

char foo()
    return 0x10;

enum E { e = 0x0f };

int main()
    return (char)(E)(e & foo());

Btw, the bug only occurs with the C++ frontend and not with plain C.
Comment 8 Mark Mitchell 2004-08-19 21:31:03 UTC
This is a duplicate of some other PR -- I'm just not sure which.

In C++, the compiler can assume that there will be no values of the enum greater
than 0xf, given the declaration.  That is why the compiler omits the bitwise-and.
Comment 9 Richard Earnshaw 2004-08-20 09:33:30 UTC
The bitwise and is applied to something that isn't an enumerated type (it's
wider than that), and it's whole purpose is to reduce the value to something
that then *can* be cast to the enumerated type.

It's not as if the user had written

0xf & static_cast<Test_Enum>(ret6666(44));

Which would be incorrect.
Comment 10 Mark Mitchell 2004-08-22 23:30:45 UTC
I agree.

Roger, I think this is a problem in fold with this code:

      /* Convert (T)(x & c) into (T)x & (T)c, if c is an integer
         constants (if x has signed type, the sign bit cannot be set
         in c).  This folds extension into the BIT_AND_EXPR.  */
      if (INTEGRAL_TYPE_P (type)
          && TREE_CODE (type) != BOOLEAN_TYPE
          && TREE_CODE (TREE_OPERAND (t, 0)) == BIT_AND_EXPR
The problem is that we turn:

  (enum E) ((unsigned int) (expr) & 0xf)


  (E) (unsigned int) (expr) & (E) 0xf

which is not valid because "expr" may not be within the range of "E".  (In this
case "E" has the range [0, 0xf].)  The original code has a well-defined meaning;
the resulting code has an "unspecified" meaning according to the C++ standard. 
I think this transformation is only safe if the values can be shown to be in
range, as would be the case if the TYPE_PRECISION of "E" was as big as that of
"unsigned int".

Therefore, I've assigned this bug to you.
Comment 13 Andrew Pinski 2004-08-25 20:58:57 UTC
Comment 14 Richard Earnshaw 2004-08-26 12:33:27 UTC
Milestone update for release note extraction.