45

I was expecting that in my following code:

#include<stdio.h> 
int main(){
    int i = 10; 
    int j = 10;

    j = ++(i | i);
    printf("%d %d\n", j, i);

    j = ++(i & i);
    printf("%d %d\n", j, i);

    return 1;
}

expressions j = ++(i | i); and j = ++(i & i); will produce lvalue errors as below:

x.c: In function ‘main’:
x.c:6: error: lvalue required as increment operand
x.c:9: error: lvalue required as increment operand   

But I surprised that above code compiled successfully, as below:

~$ gcc x.c -Wall
~$ ./a.out 
11 11
12 12   

Check the above code working correctly.

While other operators produce error (as I understand). Even bitwise operator XOR causes of an error j = ++(i ^ i); (check other operators produce an lvalue error at compilation time).

What is the reason? Is this is unspecified or undefined ? or bitwise OR AND operators are different?

compiler version:

gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)

But I believe compiler version shouldn't reason for non-uniform behavior. If ^ not compiled then | and & also not. otherwise should work for all

Its not an error with this compiler in c99 mode: gcc x.c -Wall -std=c99.

Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
  • my question is why compiled ? – Grijesh Chauhan Feb 13 '13 at 18:15
  • Don't understand your question. Why would it fail? It is perfectly right. – SwiftMango Feb 13 '13 at 18:18
  • It fails on my machine (GCC 4.7). Which GCC are you using? – antonijn Feb 13 '13 at 18:20
  • Fails for me on both clang and gcc. – zch Feb 13 '13 at 18:21
  • 4
    It works with GCC 4.4 for me... It seems to be a corrected bug. – md5 Feb 13 '13 at 18:21
  • Maybe I experienced a syntax error (perhaps even a semantic error) parsing the above text. It does not compile here (gcc-4.6.1) and it should not compile. – wildplasser Feb 13 '13 at 18:21
  • 1
    @texasbruce it should not compiled be cause `(i|i)` is expression not a variable see `++(i|i)` something like `(i|i) = `(i|i)` + 1` that is lvalue error. – Grijesh Chauhan Feb 13 '13 at 18:21
  • 1
    What is the __specific__ compiler version you are using? I've tried two different gcc releases, clang, and intel. All emit a compiler diagnostic. – Blastfurnace Feb 13 '13 at 18:22
  • @GrijeshChauhan Yeah you are right. Just realized. – SwiftMango Feb 13 '13 at 18:22
  • @Blastfurnace Just mention in question but But I believe compiler version shouldn't reason for non-uniform behavior. If ^ not compiled then | and & also not. otherwise true for all. – Grijesh Chauhan Feb 13 '13 at 18:25
  • The reason we're asking for the specific compiler version is, `++(i|i)` and `++(i&i)` *should* trigger semantic errors, so this is a *bug* in whatever compiler you're using. Bugs do not have to behave uniformly. – zwol Feb 13 '13 at 18:26
  • Looks like you are using an old and busted compiler. Who cares why it's not broken in a consistent manner. Upgrade your tools and move on. – Blastfurnace Feb 13 '13 at 18:26
  • @Blastfurnace May be. Are your sure? – Grijesh Chauhan Feb 13 '13 at 18:28
  • 2
    The behavior you observe doesn't occur in __newer__ gcc releases or other compilers. Why are you having a difficult time understanding this? – Blastfurnace Feb 13 '13 at 18:29
  • I can't quote chapter and verse at you from this computer (which doesn't have a copy of C99 on it) but I do know for sure that your code _should not_ compile and that this indicates a bug in the compiler you are using. – zwol Feb 13 '13 at 18:30
  • 6
    @Blastfurnace It is _conceivable_ that the OP's specific version of gcc is correct and everyone else is wrong. It's extraordinarily unlikely in this case, but it's still true that "_should_ this compile" questions can only be answered by reference to the language standard, not the behavior of compilers. (Today I am the kind of pedant who insists on a difference between "this is supposed to be an invalid program" and "this isn't technically an invalid program but good luck finding a compiler that supports it".) – zwol Feb 13 '13 at 18:33
  • 1
    Looks like this is a problem with GCC 4.4.x (I see the problem with 4.4.3) - I'm not sure if earlier releases also have the problem or if it was introduced in 4.4. It's clearly been fixed in more recent versions of GCC. – Michael Burr Feb 13 '13 at 18:33
  • @Blastfurnace see I have knowledge for compiler writing so my concept says that if its false(or bug) it should be for all. other wise something far my understanding. And I feels Mr. abelenky has answered. – Grijesh Chauhan Feb 13 '13 at 18:34
  • 1
    @GrijeshChauhan Ah, I see where your confusion is coming from. Please read through the function at http://gcc.gnu.org/viewcvs/trunk/gcc/fold-const.c?revision=195888&view=markup#l9792 -- you don't have to understand the code in detail, in fact you can probably just pay attention to the comments. This should demonstrate to you that production compilers contain enormous piles of ad-hoc heuristics which do not necessarily apply consistently to all related language constructs. – zwol Feb 13 '13 at 18:42
  • @Zack Thanks a lot Zack. I am reading it. Will report back if you find some answer. Thanks – Grijesh Chauhan Feb 13 '13 at 18:47
  • 1
    @GrijeshChauhan If you think you need to spend more than a couple minutes looking at that code you have misunderstood the point I was trying to make by pointing you at it. – zwol Feb 13 '13 at 19:00
  • @Zack Thanks Zack you for your answer and GCC link. Check my answer too. – Grijesh Chauhan Feb 14 '13 at 17:30

5 Answers5

28

You are right that it should not compile, and on most compilers, it does not compile.
(Please specify exactly which compiler/version is NOT giving you a compiler error)

I can only hypothesize that the compiler knows the identities that (i | i) == i and (i & i) == i and is using those identities to optimize away the expression, just leaving behind the variable i.

This is just a guess, but it makes a lot of sense to me.

abelenky
  • 63,815
  • 23
  • 109
  • 159
25

This is a bug that has been addressed in more recent GCC versions.

It's probably because the compiler optimizes i & i to i and i | i to i. This also explains why the xor operator didn't work; i ^ i would be optimized to 0, which is not a modifiable lvalue.

antonijn
  • 5,702
  • 2
  • 26
  • 33
17

C11 (n1570), § 6.5.3.1 Prefix increment and decrement operators
The operand of the prefix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue.

C11 (n1570), § 6.3.2.1 Lvalues, arrays, and function designators
A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const- qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const- qualified type.

C11 (n1570), § 6.3.2.1 Lvalues, arrays, and function designators
An lvalue is an expression (with an object type other than void) that potentially designates an object.

C11 (n1570), § 3. Terms, definitions, and symbols
Object: Region of data storage in the execution environment, the contents of which can represent values

As far as I know, potentially means "capable of being but not yet in existence". But (i | i) is not capable of referencing a region a data storage in the execution environment. Therefore it is not an lvalue. This seems to be a bug in an old gcc version, fixed since. Update your compiler!

md5
  • 23,373
  • 3
  • 44
  • 93
  • 2
    Thanks for digging up standardese, but could you please also quote the definition of "potentially designates an object" and the constraints for the `++` operator (which is where it'll say that the operand has to be an lvalue)? Just to eliminate all doubt. – zwol Feb 13 '13 at 18:34
  • There should be language somewhere near 6.3.2.1 that elaborates on exactly what expressions can "potentially designate an object", that's what I really want to see here. – zwol Feb 13 '13 at 18:44
  • @Zack I'd think that's to exclude literals from the definition of object. For example, the literal `"hello"` is an lvalue and of *type* object but not an object. Hence, the cautionary words "potentially an object*. But I don't know if there's an explicit defintion of it in the standard. – P.P Feb 13 '13 at 18:47
  • @Kirilenko Hi Kirilenko! you answer was quite helpful to me :) thanks – Grijesh Chauhan Feb 14 '13 at 17:28
7

Just a follow-up to my question . I added elaborate answer so that one can find it helpful.

In my code expressions j = ++(i | i); and j = ++(i & i); are not caused for lvalue error ?

Because of compiler optimization as @abelenky answered (i | i) == i and (i & i) == i. That is exactly CORRECT.

In my compiler (gcc version 4.4.5), any expression that includes single variable and result is unchanged; optimized into a single variable (something called not an expression).

for example:

j = i | i      ==> j = i
j = i & i      ==> j = i
j = i * 1      ==> j = i
j = i - i + i  ==> j = i 

==> means optimized to

To observe it I written a small C code and disassemble that with gcc -S.

C-Code: (read comments)

#include<stdio.h>
int main(){
    int i = 10; 
    int j = 10;
    j = i | i;      //==> j = i
        printf("%d %d", j, i);
    j = i & i;      //==> j = i
        printf("%d %d", j, i);
    j = i * 1;      //==> j = i
    printf("%d %d", j, i);
    j = i - i + i;  //==> j = i
    printf("%d %d", j, i);
}

assembly output: (read comments)

main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $10, 28(%esp)   // i 
    movl    $10, 24(%esp)   // j

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax  //j = i
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf  

In above assembly code all expressions converted into following code:

movl    28(%esp), %eax  
movl    %eax, 24(%esp)

that is equivalent to j = i in C code. Thus j = ++(i | i); and and j = ++(i & i); are optimized to j = ++i.

Notice: j = (i | i) is a statement where as expression (i | i) not a statement (nop) in C

Hence my code could successfully compiled.

Why j = ++(i ^ i); or j = ++(i * i); , j = ++(i | k); produce lvalue error on my compiler?

Because either expression has the constant value or not modifiable lvalue (unoptimized expression).

we can observe using asm code

#include<stdio.h> 
int main(){
    int i = 10; 
    int j = 10;
    j = i ^ i;
    printf("%d %d\n", j, i);
    j = i - i;
    printf("%d %d\n", j, i);
    j =  i * i;
    printf("%d %d\n", j, i);
    j =  i + i;
    printf("%d %d\n", j, i);        
    return 1;
}

assembly code: (read comments)

main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    $10, 28(%esp)      // i
    movl    $10, 24(%esp)      // j

    movl    $0, 24(%esp)       // j = i ^ i;
                               // optimized expression i^i = 0
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    $0, 24(%esp)      //j = i - i;
                              // optimized expression i - i = 0
    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax    //j =  i * i;
    imull   28(%esp), %eax
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    28(%esp), %eax   // j =  i + i;
    addl    %eax, %eax
    movl    %eax, 24(%esp)

    movl    $.LC0, %eax
    movl    28(%esp), %edx
    movl    %edx, 8(%esp)
    movl    24(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf

    movl    $1, %eax
    leave

Hence so this produce an lvalue error because operand is not a modifiable lvalue. And non-uniform behavior is due to compiler optimization in gcc-4.4.

Why new gcc compliers(or most of compilers) produce an lvalue error?

Because evaluation of expression ++(i | i) and ++(i & i) prohibits actual defination of increment(++) operator.

According to Dennis M. Ritchie's book "The C Programming Language" in section "2.8 Increment and Decrement Operators" page 44.

The increment and decrement operators can only be applied to variables; an expression like (i+j)++ is illegal. The operand must be a modifiable lvalue of arithmetic or pointer type.

I tested on new gcc compiler 4.47 here it produces error as I was expecting. I also tested on tcc compiler.

Any feedback/ comments on this would be great.

Community
  • 1
  • 1
Grijesh Chauhan
  • 57,103
  • 20
  • 141
  • 208
1

I don't think at all it is an optimization error, because if it was, then there should not be any error in the first place. If ++(i | i) is optimized to ++(i), then there should not be any error, because (i) is an lvalue.

IMHO, I think that the compiler sees (i | i) as an expression output, that, obviously, outputs rvalue, but the increment operator ++ expects an lvalue to change it, thus the error.

Ghasan غسان
  • 5,577
  • 4
  • 33
  • 44
  • Ghasan `++(i)` is not lvalue. Thanks :) – Grijesh Chauhan Mar 16 '13 at 17:33
  • @GrijeshChauhan I didn't say `++(i)` is an lvalue, but `(i)`. That's why claiming that the compiler optimizes the expression from `(i | i)` to `(i)` is not true, because `++(i)` is valid. – Ghasan غسان Mar 16 '13 at 17:58
  • Ghasan Sorry I misunderstood you. Ok read my answer. What I shown that.. in `GCC 4.4.5` `++(i|i)` is optimized to `++(i)` that is the reason code is working on `4.4.5` But evaluation of expression `++(i|i)` is against the definition of `++` operator in C So later in new version of GCC the `++(i|i)` produce an lvalue error. Only. – Grijesh Chauhan Mar 16 '13 at 18:03