This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH]: Fix PR tree-optimization/21407


Geoff Keating wrote:

On 11/05/2005, at 11:39 AM, Mark Mitchell wrote:


If you asked someone twenty years ago whether these kinds of operations were legal, the answer would have been uniformly yes. Now, opinions differ, because people are seeing more benefits of restricting these operations. But, the standards still don't say.

  struct I {
    virtual void f(); // I is not a POD.
    int a;
    int b;
  };

  void g() {
    I i;
    int *a_p = &i.b;
    /* 8 is a magic number! */
    I* i_p = (I*)((char *) a_p - 8);
    i_p->a = 3;
  }

There's no casting between pointers and integers. There are reinterpet_casts between pointers. The mapping is implementation- defined, but, in practice, all implementations on, say, IA32, are going to leave the bit-pattern unchanged. I just don't see anything that says conclusively that this code is invalid.


In C++, this code clearly invokes unspecified behaviour, initially with the cast of a_p to 'char *'.

Many people would say, reasonably enough, that specifying the behavior is specifying (a) the type of the result, and (b) the value. After all, that's how the descriptions of expressions are written. So, as long as we specify that the result of the conversion is that you get a "char *" with the same bit-pattern as "a_p", then there's nothing more for us to say.


The question is what you can do with a pointer of that type and value.

You can have Nick's alternative #1, and say that you can form pointer values by any means, and if they happen to point at something of the type you're dereferencing, then you're OK.

You can have Nick's alternative #3, and give dynamic history to pointers. That's what the optimizer folks would like; since a_p points to object with sizeof(int) bytes, you can never derive from it a pointer pointing outside that region.

But, certainly, nothing in the current standard suggests that what you can do with a pointer whose value compares equal to that of i.a depends on whether you got that pointer by writing "&i.a", or by pointer arithmetic, or by "(int *)0x<right address here>".

(However, if you've never taken the address of any part of "i", then there's no way for a conforming program to know what address i.a has, so we can just assume that they didn't get the right value if they try to just guess a number. And, therefore, we can avoid actually allocating memory. But, if you've taken the address of some part of "i", then the program can know the address of i.a.)

Consider:

  struct S {
   virtual void f();
   int a;
   int b;
  };

  void f(int *a_p) {
    if (*a_p != 3) abort();
  }

  void g() {
    S s;
    s.a = 3;
    f((int*) ((char *)&s.b - ((char*) &s.b - (char*) &s.a))));
  }

The only thing that can possibly make this code abort is that we're not allowed to manipulate the pointer to s.b like this. That's going to be rather surprising, given that we can do an algebraic simplication and just get:

f ((int *) (char*) &s.a);

which nobody is proposing should result in an abort.

The code is indeed invalid under Nick's model #3, but I sure don't see anything in the C++ standard to suggest that anybody expected this code to fail.

--
Mark Mitchell
CodeSourcery, LLC
mark@codesourcery.com
(916) 791-8304


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]