Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicit decomposition with .reloc #38

Open
SebastienBini opened this issue Nov 14, 2023 · 0 comments
Open

Implicit decomposition with .reloc #38

SebastienBini opened this issue Nov 14, 2023 · 0 comments

Comments

@SebastienBini
Copy link
Owner

SebastienBini commented Nov 14, 2023

I don't know if this proposal has any future at this point, but I've had an idea that simplifies several aspects of the proposal. This is .reloc which allows to relocate a direct data-member or direct base of a local variable (or prvalues).

struct T : public B { D _d; };
void foo() {
  T a;
  func1(a.reloc _d);
  func2(a.reloc B);
}

From the moment .reloc is used on a complete object, the object is considered to be "decomposed" until it reaches its end of scope. When an object is decomposed:

  • All its direct subobjects are considered as individual complete objects on their own (in particular, their address is unchanged).
  • The destructor of the object is not called.
  • If the direct bases of the object had a vtable, then the vtable point to that of the parent.
  • The program is ill-formed if the variable is reused again in all code path up to its end of scope, unless it is used to designate a data-member or a member function, or in type expressions.
  • All direct data-members can be accessed normally through obj.data-member unless the data member was already relocated.
  • All base data-members and functions can be accessed or called normally unless the direct base was already relocated.
  • Virtual bases cannot be relocated with the .reloc syntax although they can still be accessed normally.
  • The destructor of a direct base which virtually inherit (directly or not) from another class, must not call the destructor of the virtual bases. Likewise, the relocation constructor of such a direct base must not call the destroy the virtual bases when this decomposed direct base is used as a source.

A decomposition is ill-formed if any of the following is true:

  • some subobjects are inaccessible (they all need to be since they may be destructed if not relocated) ;
  • the destructor of the object is user-provided (i.e. not default generated) OR the decomposition happens from a function which does not have sufficient access privilege to access the private elements of the object.

We might strengthen this last criterion by imposing no user defined SFM instead of just the destructor.

This simplifies several aspects of the proposal.

Relocation constructor can be written by hand:

T(T other) : B{other.reloc B}, _d{other.reloc _d} {}

Subobjects which are not relocated in the member init list are not destroyed like before, and can still be used in the function body. If they are not relocated by the function body, then they are destroyed when the constructor exits.

About virtual bases: the most derived class is still the one that constructs the virtual bases. The following stands in a relocation constructor:

  • a virtual base can never be relocated. It can still be moved to build another instance with std::move.
  • the relocation constructor must know whether the instance being constructed is in charge of constructing the virtual bases (like any other constructor), and must also know whether the instance being relocated owns the virtual bases.
  • if the instance being constructed is in charge of constructing the virtual bases, then their constructor is called.
  • if the instance being relocated owns the virtual bases, then the destructor of the virtual bases is called when the constructor exits.

Decomposition functions no longer need to be a thing:

class T : public B
{
auto get_all(this T self)
{
  return std::pair{self.reloc B, self.reloc _d};
}
// ....
};

ABI consideration

If decomposition happens on a value parameter (p) from a function (f) whose ABI does not permit the destruction of p from f, then we need to provide a work-around.

We propose that a local copy of p (preferably move-constructed) may be created right before f body is entered, actually impersonating p. Note that the local copy cannot be constructed from relocation as the ABI would not permit it either.

void foo(T bar)
{
  func(bar.reloc _d);
}
// if foo ABI does not permit the destruction of bar by foo, then this is desugared into
void foo(T __bar)
{
  auto bar = reloc __bar; // will likely call the move constructor
  func(bar.reloc _d);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant