-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Summary of issue:
While discussing partial and Destroy for #6161 (also related to #6124), virtual destruction was lightly touched on but mostly as a "we should file a question about it".
Here's the a few related design questions around trivial destruction:
- Can code query whether a given type T trivially destructible?
- Example use case: Does an array of T require per-element destruction (allowing cheap destruction of arrays of trivial types)?
- Can a type T require that it (and any base types) are trivially destructible?
- Example use case: Ensure that type T is trivially destructible so that it can take advantage of performance improvements from (1).
- Can a type T require that children are trivially destructible?
- Example use case: Create a family of types descended from T that all have the same performance characteristic.
- In theory any API accepting the family of types could include trivial destructibility as a constraint if (1) is possible, although it'd be more verbose.
I'm hoping this captures the possible queries developers might want.
Details:
In the past, it's been discussed that something like a Destroy impl with an no-op fn Op might be "trivial" destruction. However, the implementation of fn Op is opaque and hard to condition on. Similarly an empty Destructor impl doesn't enforce any constraints on parents; and the existence of a Destructor impl at all probably means non-trivial destruction, just with no extra calls (actually preventing users from relying on trivial destruction). As a consequence, I think there are feasibility issues with conditioning trivial destruction on the contents of the definition of either Destroy.Op or Destructor.Op.
Regarding (3), zygoloid suggested a generic approach might be possible wherein a parent type says something like "all children must include a mixin, and this particular mixin says the child is trivially destructible". As a consequence, it may make sense to delay any answer on (3).
Any other information that you want to share?
Given a blanket impl of an empty TrivialDestroy interface (added using an approach similar to Destroy, discussed in #6124), I think (1) could look like impls TrivialDestroy and (2) could look like impl as TrivialDestroy {}.
As noted above, I think delaying any decision on (3) probably makes sense (perhaps only revisiting if there's a more concrete use-case, and there's still no answer).
Regarding diagnosing issues, I think the Destroy blanket impl (and DynamicDestroy) should still be generated for TrivialDestroy types, and could error in the case of (2) if there's an issue between expected trivia l destruction and actual trivial destruction (which would include an impl as Destructor). Per #6124 there's mention to behave as if "synthesizing an impl at the end of the class", so maybe that's enough? This might shift depending on what choices are made for an opt out "not destroyable" mechanism... I think the main goal should just be that it's diagnosed if a type impls TrivialDestroy and is not trivially destructible, without requiring an attempt to destroy it (i.e. it should error on type definition, not first use).