Re: [Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-24 Thread Gabriel Dos Reis
mark at codesourcery dot com [EMAIL PROTECTED] writes:

| --- Comment #22 from mark at codesourcery dot com  2006-10-04 05:39 
---
| Subject: Re:  [4.0/4.1/4.2 Regression] placement new
|  does not change the dynamic type as it should
| 
| ian at airs dot com wrote:
|  --- Comment #21 from ian at airs dot com  2006-10-03 23:44 ---
|  In C a general allocation function should work with a char array.  A 
specific
|  allocation function should use a union.  I don't think there are many 
existing
|  exceptions to these guidelines.
| 
|  So I don't see a serious problem in C either.  Am I missing something/
| 
| I think there are two remaining issues:
| 
|int i;
|*(float*)(i) = 7.0;
| 
| IIUC, Mike's position is that this is valid -- and that, in fact, after 
| this point i can no longer be accessed as an int.  Do you agree?  

I don't see how that code is supported by the paragraphs quoted by Mike.
There was a recent discussion (at most two weeks ago) on the C++
standard reflector -core about similar topic.  I do agree with Mark
that the proper way to resolve this is to go to the C++ committee.
It would be incredible that C++ was less type-strict than C.

-- Gaby




[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-09 Thread rguenth at gcc dot gnu dot org


--- Comment #23 from rguenth at gcc dot gnu dot org  2006-10-09 08:22 
---
One point to remember is that C does not allow re-using of storage with a
different type (which is what PR29272 is about and why that testcase is
invalid).

The storage type is either the declared one or the one assigned by virtue of
the
first assignment (or memcpy).  So,

 int i;
 float f;
 memcpy(f, i, sizeof(f));

is valid, it doesn't change fs dynamic type but assigns it the bit-pattern
of i.

What the C++ standard seems to imply is that the storage type of a bunch of
memory (or an object with automatic storage) changes on assignment.  So,
indeed for C++ re-ordering writes is not allowed, and escaping pointers
must be assumed to change dynamic type.

So for C++ and 4.2 the best solution looks like to disable type based aliasing
completely.  For C I'm not sure the behavior the standard mandates is very
appealing - at least changing dynamic type via the use of memcpy should be
allowed as a GCC extension (like we allow type-punning via the union trick).


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-03 Thread ian at airs dot com


--- Comment #19 from ian at airs dot com  2006-10-03 16:03 ---
Mike suggests: it would appear that it is unsafe to reorder writes of
otherwise non-conflicting types past each other as type based analysis alone
isn't enough to ensure they don't conflict.

That would be bad in the general case.  It would not be as bad as prohibiting
the reorder of reads and writes, but it would be bad.  Let's find a way such
that we do not have to do that.

Fortunately I believe that in a correct program we only have a problem when we
can actually see the placement new (can any disprove that)?  I personally don't
have a problem with saying that placement new is special.  When placement new
is used, it has to move the pointer into alias set 0.

With regard to Mark's comment #15, the problem here is not heap allocation, as
I see it.  The C standard explains how to use heap allocation: use a char*
pointer.  The problem is doing something akin to heap allocation without using
a char* pointer.


-- 

ian at airs dot com changed:

   What|Removed |Added

 CC||ian at airs dot com


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-03 Thread mark at codesourcery dot com


--- Comment #20 from mark at codesourcery dot com  2006-10-03 16:13 ---
Subject: Re:  [4.0/4.1/4.2 Regression] placement new
 does not change the dynamic type as it should

ian at airs dot com wrote:

 Fortunately I believe that in a correct program we only have a problem when we
 can actually see the placement new (can any disprove that)?  I personally 
 don't
 have a problem with saying that placement new is special.  When placement new
 is used, it has to move the pointer into alias set 0.

What about in C, as opposed to C++?

I agree that the obvious special cases are access through char*, arrays 
of characters, and placement new.  But, I'm afraid that there are lots 
of other allocation functions out there that are not spelled operator 
new, and requiring decoration for them isn't something that (as far as 
I know) other compilers require.


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-03 Thread ian at airs dot com


--- Comment #21 from ian at airs dot com  2006-10-03 23:44 ---
In C a general allocation function should work with a char array.  A specific
allocation function should use a union.  I don't think there are many existing
exceptions to these guidelines.

I think that code like that in PR 29272, which casts Node* to void* to Foo*, is
an unusual case.  And I believe we can handle that code correctly because of
the use of memcpy.  And if the code didn't use memcpy it would be clearly
noncomformant--which isn't to say that we should deliberately break it, but we
don't need to try extra hard to make it work.

So I don't see a serious problem in C either.  Am I missing something/


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-03 Thread mark at codesourcery dot com


--- Comment #22 from mark at codesourcery dot com  2006-10-04 05:39 ---
Subject: Re:  [4.0/4.1/4.2 Regression] placement new
 does not change the dynamic type as it should

ian at airs dot com wrote:
 --- Comment #21 from ian at airs dot com  2006-10-03 23:44 ---
 In C a general allocation function should work with a char array.  A specific
 allocation function should use a union.  I don't think there are many existing
 exceptions to these guidelines.

 So I don't see a serious problem in C either.  Am I missing something/

I think there are two remaining issues:

   int i;
   *(float*)(i) = 7.0;

IIUC, Mike's position is that this is valid -- and that, in fact, after 
this point i can no longer be accessed as an int.  Do you agree?  Or 
do you think that this is (should be?) undefined behavior?

I think you are saying that:

   int i;
   new (i) float;

is valid, and that after this point accessing i as an int is invalid. 
  Is that correct?  If so, I assume that you conider the following C 
code valid as well:

   int i;
   float f;
   memcpy (i, f, sizeof (i));

?

Also, in C++, given:

   extern void f(int *);
   void g() {
 int i;
 f(i);
 /* HERE */
   }

do you believe that at the point marked HERE the dynamic type of i is 
indeterminate?  I think that if we allow the examples above, we have to 
assume that something may have reset the dynamic type of i here, 
meaning that we cannot assume that future stores through float * in 
the function do not alias i.


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-02 Thread mrs at apple dot com


--- Comment #16 from mrs at apple dot com  2006-10-02 09:32 ---
The dynamic type of the object at i is indeed float and has the value 7.0 (iff
align and sizes work out).  We permitted this so that can can do:

  class C { ... };
  char buf[1024];
  new (buf[n]) C

and have the dynamic type of the storage be C.  You can know the dynamic type
is C, by doing a virtual dispatch, or by asking for the name of the type with
rtti.

Likewise, you can do:

  new (i) float(7.0)

with the same effects.  The dynamic type is float, and the value is 7.0, and
this is in all ways the same as:

  *(float*)i = 7.0

Thinking about this some more, it would appear that it is unsafe to reorder
writes of otherwise non-conflicting types past each other as type based
analysis alone isn't enough to ensure they don't conflict.  It might be
reasonable to add the C rules that the dynamic type _must_ match the static
(declared) type, if there is one for types other than pointer or pointer to
member the the language standard and to clarify the dynamic type of an object
when a bitwise copy of the object is made to better match the C rules.

Reads should be fine as they are required to be the same type as the write (or
character).  In practice, if the optimizer can't see the placement new, it
can't reorder things so this is safe; if it can see it, then it should do value
based analysis in addition to type analysis and conclude they conflict (or can
conflict) and still avoid doing the wrong thing.

I do agree, that for now, this isn't a bug in the library.  It could be made a
bug in the library by requring a placement new operator as the only way in
which storage can be reused as a different dynamic type.


-- 

mrs at apple dot com changed:

   What|Removed |Added

 CC||mrs at apple dot com


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-02 Thread mmitchel at gcc dot gnu dot org


--- Comment #17 from mmitchel at gcc dot gnu dot org  2006-10-02 17:48 
---
I disagree with Mike's analysis.  I certainly understand the goals of placement
new, but I don't think that anyone intended this code:

  int i;
  *(float *)i = 7.0;

to be valid.  I don't even think people intended this variant:

  int i;
  new (i) float(7.0);

to be valid.  Note that a consequence of allowing this is that:

  int f() {
   int i = 3;
   new (i) float(7.0);
   return i;
  }

is undefined -- not because of the overwriting of i, but because the return
statement reads memory of the wrong type.  Mike's position is that accessing a
variable using its static type is not always safe; I find this bizarre.

My personal opinion is that the authors of the standard just didn't think of
all the possible interactions of the various aspects.  I believe placement new
was intended to handle only arrays of characters, consistent with the special
dispensation given to character pointers.

However, I don't think my differences with Mike can be resolved without going
directly to the standards committee.  Fortunately, from the compiler point of
view, I'm saying punt and Mike is saying punt harder.  So, it's probably
just a question of in how many cases we decide to disable type-based alias
analysis.


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-02 Thread mrs at apple dot com


--- Comment #18 from mrs at apple dot com  2006-10-02 19:28 ---
What is your position based upon?  Mine is based upon having been in the room
when we decided what the C rules probably were, what the C++ rules could be and
the up side and down side of each choice and where we decided what our intent
was going to be and lots of word smithing sessions as well.  You can see some
of the depth with which we discussed it in our formulation of the wording in
the standard:

6 Similarly,  before the lifetime of an object has started but after the
  storage which the object will occupy has been allocated or, after  the
  lifetime  of  an  object  has  ended  and before the storage which the
  object occupied is reused or released, any lvalue which refers to  the
  original  object may be used but only in limited ways.  Such an lvalue
  refers to allocated  storage  (_basic.stc.dynamic.deallocation_),  and
  using the properties of the lvalue which do not depend on its value is
  well-defined.  If  an  lvalue-to-rvalue  conversion  (_conv.lval_)  is
  applied  to such an lvalue, the program has undefined behavior; if the
  original object will be or was of a non-POD class  type,  the  program
  has undefined behavior if:

  --the lvalue is used to access a non-static data member or call a non-
static member function of the object, or

  --the lvalue is implicitly converted (_conv.ptr_) to a reference to  a
base class type, or

  --the   lvalue   is   used   as   the   operand   of   a   static_cast
(_expr.static.cast_) (except when the conversion  is  ultimately  to
char or unsigned char), or

  --the   lvalue   is   used   as   the   operand   of   a  dynamic_cast
(_expr.dynamic.cast_) or as the operand of typeid.

7 If, after the lifetime of an object has ended and before  the  storage
  which  the object occupied is reused or released, a new object is cre-
  ated at the storage location which the  original  object  occupied,  a
  pointer that pointed to the original object, a reference that referred
  to the original object, or the name of the original object will  auto-
  matically  refer  to  the new object and, once the lifetime of the new
  object has started, can be used to manipulate the new object, if:

  --the storage for the new object exactly overlays the storage location
which the original object occupied, and

  --the  new object is of the same type as the original object (ignoring
the top-level cv-qualifiers), and

  --the original object was a most derived  object  (_intro.object_)  of
type  T  and the new object is a most derived object of type T (that
is, they are not base class subobjects).  [Example:
  struct C {
  int i;
  void f();
  const C operator=( const C );
  };
  const C C::operator=( const C other)
  {
  if ( this != other )
  {
  this-~C(); // lifetime of *this ends
  new (this) C(other);// new object of type C created
  f();// well-defined
  }
  return *this;
  }
  C c1;
  C c2;
  c1 = c2;// well-defined
  c1.f(); // well-defined; c1 refers to a new
object of type C
 --end example]

8 If a program ends the lifetime of an object  of  type  T  with  static
  (_basic.stc.static_)  or automatic (_basic.stc.auto_) storage duration
  and if T has a non-trivial destructor,35) the program must ensure that
  an object of the original type occupies  that  same  storage  location
  when  the implicit destructor call takes place; otherwise the behavior
  of the program is undefined.  This is true even if the block is exited
  with an exception.

We explcitly did consider overlaying one type with a completely different type
and did so intend for the standard to allow that.  Further, our intent of
saying that automatics and objects with static duration could be so changed
provided certain conditions were met was meant no only to state that this could
be done, but to state the exact sitations in which it was defined to do so.

 Mike's position is that accessing a variable using its static type is not 
 always safe; I find this bizarre.

Welcome to C++, let me quote the standard:

8 If a program ends the lifetime of an object  of  type  T  with  static
  (_basic.stc.static_)  or automatic (_basic.stc.auto_) storage duration
  and if T has a non-trivial destructor,35) the program must ensure that
  an object of the original type occupies  that  same  storage  location
  when  the implicit destructor call takes place; otherwise the behavior
  of the program is undefined.

If we didn't mean exactly what we wrote, why on earth would we write it?  Hint,
in this, we meant exactlty what was written.  I was there, in every single
meeting where we talked about it.  I know what our intent was, and I 

[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-01 Thread mmitchel at gcc dot gnu dot org


--- Comment #15 from mmitchel at gcc dot gnu dot org  2006-10-01 18:39 
---
I cannot see any plausible way to argue that this is a library bug. 
Implementation of placement new should not need barriers or other compiler
intrinsics.  It's twisted to argue that the C++ standard itself would prevent
implementation of placement new without non-standard facilities, since the
people that wrote the standard all certainly expected the implementation to be
just:

  void *operator new(size_t, void *p) {
return p;
  }

Variations on that definition also occur in various user-defined new operators,
including those for class-scoped operators.  I don't think we can reasonably
ask people to change this code, which works with every other C++ compiler. 
Therefore, the only possible conclusion is that either the compiler is buggy or
that the examples are invalid.  Unfortunately, I don't think Andrew's example
is invalid.

There are two possible approaches to fixing the compiler: either (a) the C++
front end should mark all new operators as special, in some way that the middle
end then uses to avoid this over-optimization, or (b) relax the aliasing rules
used by the middle end.

I think the right solution is (b).  Fundamentally, I'm not sure that the object
models in C and C++ are sufficiently well-defined to permit the kind 
of optimization that we are discussing.  I also think that people use functions
not named operator new to do the kinds of things being discussed (i.e., to
allocate blocks of memory) and that C and C++ are intended to support that
usage.  Certainly, in C, people can't use operator new.  So, I'd probably
suggest that we relax the aliasing rules, at least for heap-allocated objects.

As an example of the kind of problems the standard poses, [basic.life] says:

The  lifetime  of  an object is a runtime property of the object.  The
  lifetime of an object of type T begins when:

  --storage with the proper alignment and size for type T  is  obtained,
and

  --if  T is a class type with a non-trivial constructor (_class.ctor_),
the constructor call has completed.

  The lifetime of an object of type T ends when:

  --if T is a class type with a non-trivial  destructor  (_class.dtor_),
the destructor call starts, or

  --the storage which the object occupies is reused or released.

So, by that definition, it might seem that:

  int i = 3;
  *((float *)(i)) = 7.0;

is valid; we're reusing the int memory, as a float, and it has the right
size and alignment (on most systems).  But, that would effectively nullify the
[basic.lval] language that suggests that you can't access an object through a
type other than its dynamic type -- or, at least, it would limit such access to
writes.  I don't think anyone would expect the dynamic type of i to be
float  in this example, even if someone had written:

  new (i) float;

Surely, the dynamic type of something explicitly declared int must be int. 
But, for something with dynamic storage duration, perhaps we should assume that
any write to the storage, with a type other than the one that it already has,
forces it to possibly alias things of both types.


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-10-01 Thread mmitchel at gcc dot gnu dot org


-- 

mmitchel at gcc dot gnu dot org changed:

   What|Removed |Added

   Priority|P3  |P1


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-09-29 Thread pinskia at gcc dot gnu dot org


--- Comment #13 from pinskia at gcc dot gnu dot org  2006-09-29 16:58 
---
And here is a testcase that fails also for 4.0.0 and self contained also:
#include new
int foo(int n, int *f, long *f1)
{
  long t = 0;
  *f = 1;
  long *fp = new(f) long;
  *fp = 1;
  for (int i=0; in; ++i)
{
  t += *fp;
  int *b = new (f) int;
  *b = i + *f1;
  fp = new (f) long;
  *fp = t*t;
}
  t+=*fp;
  return t+*f1;
}

extern C void abort ();

int main(void)
{
  if (sizeof(int) != sizeof(long)
return 0;
  int *a = new int;
  long *b = new long;
  if (foo(2, a, b) != 6)
abort ();
  return 0;
}


-- 

pinskia at gcc dot gnu dot org changed:

   What|Removed |Added

  Known to fail||4.0.0 4.1.0 4.2.0
  Known to work|4.1.1   |3.4.0
Summary|[4.2 Regression] placement  |[4.0/4.1/4.2 Regression]
   |new does not change the |placement new does not
   |dynamic type as it should   |change the dynamic type as
   ||it should


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286



[Bug libstdc++/29286] [4.0/4.1/4.2 Regression] placement new does not change the dynamic type as it should

2006-09-29 Thread pinskia at gcc dot gnu dot org


--- Comment #14 from pinskia at gcc dot gnu dot org  2006-09-29 17:01 
---
(In reply to comment #13)
 And here is a testcase that fails also for 4.0.0 and self contained also:
replace main with:
int main(void)
{
  if (sizeof(int) != sizeof(long))
return 0;
  int *a = new int;
  long *b = new long;
  *a = 0;
  *b = 0;
  if (foo(2, a, b) != 6)
abort ();
  return 0;
}


This is what you get when thinking about 2 steps ahead of yourself.


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286