https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94658
Bug ID: 94658 Summary: Incorrect transformation with -fstrict-aliasing Product: gcc Version: 9.3.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c Assignee: unassigned at gcc dot gnu.org Reporter: pascal_cuoq at hotmail dot com Target Milestone: --- C17 6.5:6 says: The effective type of an object for an access to its stored value is the declared type of the object, if any.88) If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access. I interpret the words “If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value” as saying that the repurposing of dynamically allocated memory is allowed in C. A write of a new value to dynamically allocated memory is not a “subsequent access that does not modify the stored value”, so it simply changes the type of the memory to the type of the lvalue being used for the write. Consider the following program, which was provided by Sorav Bansal: (Compiler Explorer link: https://gcc.godbolt.org/z/Vg25xq ) #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> int a; __attribute__((__noinline__)) int foo(float *x, int *y, int *z) { int sum = 1; for (int i = 0; i < a; i++) { *z = 0x123; *x = 3.0f; sum *= *y; *z = 0x40400300; } return sum; } int main() { char *f; f = malloc(32); if (!f) abort(); int i = 3; a = 1; foo((float*)f, &i, (int*)f); float fl; memcpy(&fl, f, 4); printf("f = %f\n", fl); } According to my interpretation above (“repurposing of dynamically allocated memory is allowed”), this program does nothing wrong. The memory location that both x and z point to is repurposed many times, but it is never read inside function foo, and the function main reads it carefully using memcpy. The behavior I expect for this program is to print: f = 3.000183 (3.000183 is the float represented by 0x40400300.) However, when -fstrict-aliasing is enabled, the compiler moves the assignment “*x = 3.0f;” after the loop and makes the compiled program print: f = 3.000000 I think the transformation is wrong here. The function foo does not invoke UB when passed z and x aliasing pointers to dynamically allocated memory, and the “naïve memory model” behavior of the function should have been preserved for such a calling context. Yes, this would mean that Clang has independently implemented the same bug, but I am still confident enough that the C standard intended to allow the repurposing of dynamically allocated memory to report this. Please accept my apologies if I am wrong. I have heard it said that the C++ standard has a different philosophy, but this is a bug report against the C compiler.