Versions with the problem: 2.96 (readhat) 3.3.1 (cygming spetial) Versions without the problem: 2.95.2 19991024 (release) 2.95.3 20010315 (release) [FreeBSD] Environment: Red Hat Linux 7.3 2.96-112 (i386-redhat-linux) CYGWIN_NT-5.1 1.5.9(0.112/4/2) 2004-03-18 23:05 i686 How to reproduce: % g++ -Wall g++_bug.cc % ./a.out 1 1 2 2 3 3 4 4 5 4 a.out: g++_bug.cc:11: void func (int): Assertion `a[1] - a[0] == r' failed. Abort (core dumped) % Expected result: % ./a.out 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 % Source Code: // Filename: g++_bug.cc #include <iostream> #include <cassert> void func(int r) { int (*a)[r] = new int [2][r]; if (r<5) a[1][0] = 0; std::cout << r << '\t' << a[1] - a[0] << std::endl; assert( a[1] - a[0] == r); delete [] a; } int main() { for (int i=1; i<10; i++) func(i); return 0; }
Fixed in 3.4.0 by rejecting the VLA: pr15508.cc: In function `void func(int)': pr15508.cc:7: error: `r' cannot appear in a constant-expression
(In reply to comment #1) > Fixed in 3.4.0 by rejecting the VLA: GCC 3.4.0 Manual saids, -Extensions to the C Language Family --Arrays of Variable Length "Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C89 mode and in C++." Do you mean this is not correct anymore?
No it is correct still what you are doing is not correct anyways. As, the type return by new int [2][r] is not int *[r] but is int **. int (*a)[r] = new int [2][r];
(In reply to comment #3) > As, the type return by new int [2][r] is not int *[r] but is int **. I beleve it is not 'int *[r]', but is 'int (*)[r]'. 'int *[r]' is a array of pointer to integer, while 'int (*)[r]' is a pointer to array of integer. In GCC 3.3.1, following error occurs compiling the code: void func(int r) { int **a = new int [2][r]; delete [] a; } a.cc: In function `void func(int)': a.cc:3: error: cannot convert `int (*)[((r - 1) + 1)]' to `int**' in initialization Does the return type of 'new int [2][r]' in GCC 3.4.0 differ from that in GCC 3.3.1?
Actually it is rejecting that expression which is correct: void func(int r) { int **a = new int [2][r]; delete [] a; }
(In reply to comment #5) > Actually it is rejecting that expression which is correct: OK. Then which is correct? a) 'int **a = new int [2][3];' b) 'int (*a)[3] = new int [2][3];' I believe b) is correct, but you don't, do you?
The point of the problem, I mentioned first, is in 'if (r<5) a[1][0] = 0;'. Size evaluation of 'int [r]' is first needed in 'a[1][0] = 0;' in func(). g++ 2.96 and 3.3.1 generate the code which evaluate the size of 'int [r]' in the 'if' block and store it. The evaluation result is re-used thereafter. But, obviously, the 'if' block is executed only when r<5. If r>=5, the evaluation result is used though it is not evaluated yet. This is the cause. Are there still any problems in my code, you think?
And that should be fixed in 3.5.0 and above.
(In reply to comment #8) > And that should be fixed in 3.5.0 and above. Thanks in advance. Can I "reopen bug"?
The comments are pretty confusing. Can we have a recap? What is exactly the bug?
(In reply to comment #10) > The comments are pretty confusing. Can we have a recap? What is exactly the bug? Thanks. I'd like to re-explain with some additional stuffs. Problem: Assertion at line 12 fails if r>=5. I have studied on assembler source code generated by "g++ -Wall -S pr15508.cc", and found that: The 'void func(int)' in the simple test program shown in the report needs the size of variable length array 'int [r]' in execution. G++ 2.96 and 3.3.1 generate codes which evaluate it in 'if' block at line 9 to get address of 'a[1][0]'. The size evaluation result for 'int [r]' is needed to get pointer 'a[1]' also at line 11 and 12. The result evaluated at line 9 is reused here. But the evaluation at line 9 is executed only when r<5. If r>=5, the evaluation result is used in spite of being not evaluated yet. As a result, the assertion at line 12 fails though it should always succeed. Also this can cause segmentation fault when trying to access *a[1]. Source Code: 1: // Filename: pr15508.cc 2: #include <iostream> 3: #include <cassert> 4: 5: void func(int r) 6: { 7: int (*a)[r] = new int [2][r]; 8: 9: if (r<5) a[1][0] = 0; 10: 11: std::cout << r << '\t' << a[1] - a[0] << std::endl; 12: assert( a[1] - a[0] == r); 13: 14: delete [] a; 15: } 16: 17: int main() 18: { 19: for (int i=1; i<10; i++) func(i); 20: return 0; 21: } Reference (func() part of pr15508.s by v2.96): .text .align 4 .globl func__Fi .type func__Fi,@function func__Fi: .LFB1: pushl %ebp .LCFI0: movl %esp, %ebp .LCFI1: subl $8, %esp .LCFI2: subl $12, %esp ; int (*a)[r] = new int [2][r]; movl 8(%ebp), %eax ; eax: r (read from 8(%ebp)) movl %eax, %eax sall $3, %eax ; eax: r*2*sizeof(int) (size of 'int [2][r]') subl $8, %eax addl $8, %eax pushl %eax .LCFI3: call __builtin_vec_new addl $16, %esp movl %eax, -4(%ebp) ; store address(a); ; if (r<5) cmpl $4, 8(%ebp) jg .L3 ; Evaluating size of 'int [r]' movl 8(%ebp), %eax ; eax: r (read from 8(%ebp)) movl %eax, %eax sall $2, %eax ; eax: r*sizeof(int); subl $4, %eax addl $4, %eax movl %eax, -8(%ebp) ; Store size of 'int [r]' to -8(%ebp) ; a[1][0] = 0; movl -4(%ebp), %eax ; eax: address(a) movl -8(%ebp), %edx ; edx: address offset of a[1][0] movl $0, (%edx,%eax) ; a[1][0] = 0 .L3: ; std::cout << r << '\t' << a[1] - a[0] << std::endl; subl $8, %esp pushl $endl__FR7ostream subl $12, %esp ; calculate 'a[1] - a[0]' movl -8(%ebp), %eax ; eax: size of 'int [r]' (restore from -8(% ebp)) addl -4(%ebp), %eax ; eax: address(a[1]) subl -4(%ebp), %eax ; eax: address(a[1]) - address(a[0]) movl %eax, %eax sarl $2, %eax ; eax: a[1] - a[0] pushl %eax subl $12, %esp pushl $9 ; '\t' subl $12, %esp pushl 8(%ebp) ; r pushl $cout .LCFI4: call __ls__7ostreami addl $20, %esp movl %eax, %eax pushl %eax call __ls__7ostreamc addl $20, %esp movl %eax, %eax pushl %eax call __ls__7ostreami addl $20, %esp movl %eax, %eax pushl %eax .LCFI5: call __ls__7ostreamPFR7ostream_R7ostream addl $16, %esp ; assert( a[1] - a[0] == r); movl -8(%ebp), %eax ; eax: size of 'int [r]' (restore from -8(% ebp)) addl -4(%ebp), %eax ; eax: address(a[1]) subl -4(%ebp), %eax ; eax: address(a[1]) - address(a[0]) movl %eax, %eax sarl $2, %eax ; eax: a[1] - a[0] cmpl 8(%ebp), %eax ; compare (a[1] - [0]) and r (read from 8(% ebp)) je .L5 pushl $__PRETTY_FUNCTION__.418 pushl $12 pushl $.LC0 pushl $.LC1 call __assert_fail .p2align 2 .L5: cmpl $0, -4(%ebp) je .L8 subl $12, %esp pushl -4(%ebp) call __builtin_vec_delete addl $16, %esp .L8: leave ret Additional Information: The code below works well. void func(int r) { typedef int vla[r]; vla *a = new vla [2]; if (r<5) a[1][0] = 0; std::cout << r << '\t' << a[1] - a[0] << std::endl; assert( a[1] - a[0] == r); delete [] a; }
(In reply to comment #1) > Fixed in 3.4.0 by rejecting the VLA: > pr15508.cc: In function `void func(int)': > pr15508.cc:7: error: `r' cannot appear in a constant-expression (In reply to comment #11) > The code below works well. > typedef int vla[r]; > vla *a = new vla [2]; I think I understood better, and also noticed fundamental issue of VLA. 'int (*)[r]' and 'int (*)[r]' may have different type each other because r is *variable*. Of cource both r's in expression 'int (*a)[r] = new int [2][r];' are obviously same. But, generally compiler cannot determine identity of the type of 'int (*)[r]' which appears more than once. Therefore, compiler should reject any code which associates VLAs declared individually. However, Code1 below is not rejected by gcc/g++. Code2 seems to be valid, but judgeing of validity is very difficult generally, and it should not be a role of compiler. So Code2 should be rejected too. Code3 can be accepted because it is asserted that r will be never changed in its scope by 'const'. Code4 can also be accepted because identity of type is claimed. Code5 reported by me should be rejected although Code6 and Code7 can be acceptable. My conclusion is that it is better to improve identity check of VLA type. Thank you. Code1: int r=5; int a[2][r]; r++; { int *b[r] = a; } Code2: int r=5; int a[2][r]; (codes which don't touch r) int *b[r] = a; Code3: int const r=5; int a[2][r]; (any code) int *b[r] = a; Code4: int r=5; typedef int vla[r]; vla a[2]; (any code) vla *b = a; Code5: void func(int r) { int (*a)[r] = new int [2][r]; delete [] a; } Code6: void func(int const r) { int (*a)[r] = new int [2][r]; delete [] a; } Code7: void func(int r) { typedef int vla[r]; vla *a = new vla [2]; delete [] a; }
The point, to me, seems to be that we (again) simply picked up an extension in g++ (this time from C99) and never really considered all the implications this may have on being consistent in all situations :-( The only cure, to me, seems to be to simply disallow VLA until some standards committee exactly defines what they are supposed to mean in C++. W.
As the code is not valid C++, we are rejecting it. Note I think this would be fixed if we decied ever to add back this support for VLAs.