Skip to content

How does partial interact with destruction? #6161

@jonmeow

Description

@jonmeow

Summary of issue:

Followup to #6124

The decision on #6124 was that abstract types do not impl Destroy. Does a partial abstract type impl Destroy?

Details:

Carbon's generally expecting people to write factory functions for parents, and those factory functions will often return partial Self for a base class. Particularly for an abstract type, where they must return partial Self.

A factory function's return type could also be something allowing failures, such as Optional(Self) or Self*?. The timing of when failure is allowed is important.

  1. partial types impl Destroy as long as their object representation does: If failure after construction starts but before it completes is to be allowed (including something like a parent type partially constructing, but a child type failing to construct), then I think a partial abstract type must be able to Destroy, in order to garbage collect the partially destructed state.

    • It would be a particular concern if a partial type could not impl Destructor, because of potential leaks. See below for further notes on this.

    • It may be worth thinking about how this interacts with the theorized Indestructible interface -- do people do impl partial T as Indestructible to mean that both T and partial T can't be Destroyed (because partial T is inherently part of T's representation), or does impl T as Indestructible imply that partial T also can't be Destroyed (just always doing the impl lookup removing partial)?

  2. partial types do not impl Destroy: it could be a language rule that once construction of partial state starts, the end result must be a complete object, and only the completed object can be destroyed.

    • Factory functions would need to rely on returned var move semantics and only starting construction of base types after they're sure construction will succeed.
  3. partial types only impl Destroy when the main type impls Destroy: This is subtly different from option (1) in that it means a partial abstract type does not impl Destroy, but a partial base type does.

    • This is noted in part for the question of Indestructible on (1) -- it's an approach that makes the partial version consistent.

Any other information that you want to share?

Regarding partial and Destructor, #6124 decided the signature of Destructor is:

class B {
  impl as Destructor {
    fn Op[ref self: Self]() { ... }
  }
}

As long as I'm asking questions about partial types, I'd like to confirm that this decision -- that destructors can only be called on non-partial types -- holds. Note destruction order probably looks something like:

  1. Call Destructor for child type
  2. Destroy members of child type
  3. Call Destructor for parent type
  4. Destroy members of parent type

Since Destructor takes a non-partial Self, it means that the parent destructor can continue to use virtual functions.

Note though that using Destructor with partial Self also implies the signature would be more like:

class B {
  impl partial Self as Destructor {
    fn Op[ref self: Self]() { ... }
  }
}

(where Self there changes from B in Destructor to partial B in Op)

or a different interface; perhaps:

interface Destructor {
  private fn Op[ref self: partial Self]() { ... }
}

Maybe #6124 should be augmented adding partial to the Destructor interface?

Metadata

Metadata

Assignees

No one assigned

    Labels

    leads questionA question for the leads team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions