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

Prevent collections from unnecessarily finalizing elems #54

Merged
merged 2 commits into from
Apr 27, 2022

Conversation

jacob-hughes
Copy link
Collaborator

Collection types such as Vec have drop methods which are
only necessary if T requires dropping. This can be managed partially by
implementing NoFinalize on Vec<T>:

unsafe impl<T: NoFinalize> NoFinalize for Vec<T> {}

However, this will return true for needs_finalize if T: !Drop but also
doesn't implement NoFinalize.

To get around this, we introduce a trait which is expected only to be
used in the standard library called OnlyFinalizeComponents.
Implementing this trait on a type indicates to the collector that it can
ignore that type's drop method, but it must still recurse through the
type's components to see if any of them need dropping.

Collection types such as Vec<T> have drop methods which are
only necessary if T requires dropping. This can be managed partially by
implementing `NoFinalize` on `Vec<T>`:

    unsafe impl<T: NoFinalize> NoFinalize for Vec<T> {}

However, this will return true for needs_finalize if `T: !Drop` but also
doesn't implement `NoFinalize`.

To get around this, we introduce a trait which is expected only to be
used in the standard library called `OnlyFinalizeComponents`.
Implementing this trait on a type indicates to the collector that it can
ignore that type's drop method, but it must still recurse through the
type's components to see if any of them need dropping.
@ltratt
Copy link
Member

ltratt commented Apr 26, 2022

To get around this, we introduce a trait which is expected only to be used in the standard library called OnlyFinalizeComponents.

Why would it only be used in the standard library?

@jacob-hughes
Copy link
Collaborator Author

That sentence is a bit misleading. It doesn't have to be, but I'm not sure I can really see a use-case outside of the standard library. Unless of course the user builds their own collection types.

In essence, I'm worried that all these traits (NoFinalize, OnlyFinalizeContents, Collectable) start really amping up the complexity of the API.

@ltratt
Copy link
Member

ltratt commented Apr 26, 2022

Ah, I see. So this PR is, presumably, a relatively small optimisation overall?

@jacob-hughes
Copy link
Collaborator Author

jacob-hughes commented Apr 26, 2022

This PR has quite a big ripple effect because almost everything is built out of the standard library types that are now optimised. However, in 99% of cases I don't see the user really needing to know that these traits exist. I guess you could think of this as Boehm's "wizards only interface".

@ltratt
Copy link
Member

ltratt commented Apr 26, 2022

Silly question: why does !Drop automatically imply NoFinalize?

@jacob-hughes
Copy link
Collaborator Author

It doesn't. Maybe this sentence is where the confusion comes from?

However, this will return true for needs_finalize if T: !Drop but also
doesn't implement NoFinalize.

Let me have a go at rewording it:

If we have a Vec<T>, and T is not droppable, but T also doesn't implement NoFinalize, then our needs_finalizer method will see that Vec<T> has a drop method and think it needs a finalizer.

If T implemented NoFinalize, then it wouldn't because of this impl:

unsafe impl<T: NoFinalize> NoFinalize for Vec<T> {}

So this PR addresses the "gap" here, by allowing Vec to be non-finalized, even when its contents isn't explicitly marked NoFinalize.

@ltratt
Copy link
Member

ltratt commented Apr 26, 2022

Mea culpa! I meant "why doesn't !Drop automatically imply NoFinalize?"

@jacob-hughes
Copy link
Collaborator Author

Because AFAIK there is no way to implement a trait based on the non-existence of another. In other words, you can't do this:

unsafe impl<T: !Drop> NoFinalize for ....

Also, it becomes even more tricky because T itself may never implement Drop, but some of its component types might.

@ltratt
Copy link
Member

ltratt commented Apr 27, 2022

Could our compiler hack thingy decide that "!Drop implies NoFinalize" even if the type system doesn't encode that directly?

@jacob-hughes
Copy link
Collaborator Author

It does in most cases. If it can't find a Drop impl for T it will recurse through T's subtypes until it finds one. If it can't find one, it will assume T is NoFinalize.

This doesn't work when you have types like Vec<T> though, because Vec<T> : Drop. The drop method on Vec just calls T::drop on its element type. So we need a way of saying: "ignore this particular drop impl, but keep recursing through the type components until you find another". This is what OnlyFinalizeComponents does.

@ltratt
Copy link
Member

ltratt commented Apr 27, 2022

I think I get it!

bors r+

@bors
Copy link

bors bot commented Apr 27, 2022

Build succeeded:

@bors bors bot merged commit 48a162e into softdevteam:master Apr 27, 2022
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

Successfully merging this pull request may close these issues.

2 participants