> On Jul 21, 2016, at 1:31 PM, Richard Smith <[email protected]> wrote:
> On 21 July 2016 at 13:20, Jason Merrill <[email protected]
> <mailto:[email protected]>> wrote:
> On Thu, Jul 21, 2016 at 2:45 PM, Richard Smith
> <[email protected] <mailto:[email protected]>> wrote:
> > On 21 July 2016 at 11:02, Jason Merrill <[email protected]
> > <mailto:[email protected]>> wrote:
> >>
> >> P0135 seems to require that we elide the copy when using the result of
> >> a function returning by value to initialize a base class subobject,
> >> but the ABI doesn't currently require that such a function avoid
> >> clobbering tail padding when initializing its return object.
> >> Thoughts?
> >
> > If the function clobbers the tail padding of its return object, at least GCC
> > and Clang will miscompile the program today, without P0135:
> >
> > #include <string.h>
> > struct X { ~X() {} int n; char d; };
> > struct Y { Y(); char c[3]; };
> > struct Z : X, virtual Y { Z(); };
> >
> > X f() { X nrvo; memset(&nrvo, 0, sizeof(X)); return nrvo; }
> > Z::Z() : Y(), X(f()) {}
> > Y::Y() : c{1, 2, 3} {}
> >
> > int main() {
> > Z z;
> > return z.c[0];
> > }
> >
> > GCC -O0 returns 1 from main, as it should. GCC -O2 and Clang (any
> > optimization level, even with -fno-elide-constructors) returns 0.
>
> Thanks for the testcase.
>
> > (It looks like Clang gets this "wrong" in two ways: first, NRVO is apprently
> > never correct on a type whose tail padding could be reused
>
> Hmm, I was thinking that the NRVO was fine, but the caller shouldn't
> elide the copy because the function might clobber tail padding. But
> that gets back to my initial question, since P0135 requires that
> elision. Avoiding NRVO here doesn't conflict with P0135, but it does
> create a new ABI requirement that existing code might violate.
>
> Given John's observation that P0135 can't even work in theory for the case of
> a base class with virtual bases, it seems like disabling P0135 for the case
> of initializing a base class of a class with vbases may be the simplest way
> forward.
We re-use tail padding of all bases, not just virtual bases. It's true that
the Itanium ABI generally initializes things in ascending address order, but
there are *two* exceptions. The first, as you've noted, is virtual bases. The
second is when the primary base class is not the first base class in
inheritance order:
struct A {
char c;
A() : c(15) {}
};
struct B {
virtual void foo() {}
char d;
};
struct C : A, B {};
int main() {
C c;
}
Here the 'A' base is allocated in the tail padding of the 'B' base. Now, 'B'
is not technically trivially-copyable, but...
Also, it's a big world, and other/alternative/future ABIs might want to do all
sorts of things. It's also not that hard to imagine future language features
that would rely on knowing whether a constructor is initializing a base
sub-object or a complete object (for example, the language could provide a way
to declare constructors that are only allowed to initialize one or the other).
It seems to me that the maximally correct thing is to disable the P0135 mandate
for the case of initializing a base sub-object, full stop. If we can define
conditions in which it's acceptable to elide the copy, great, but that should
be up to the implementation / ABI.
(Semantic features like base-only constructors wouldn't prevent us from doing
this best-effort today because adding one to an existing type is an ODR
violation anyway. We could easily adjust the ABI rule to disable in-place copy
elision into base subobjects when the chosen copy constructor is base-only or
something.)
John.
_______________________________________________
cxx-abi-dev mailing list
[email protected]
http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev