Make Trace object-safe, allowing dynamic types and slices to be garbage collected #20
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The simple part is making a dynamically dispatched version of 'Trace' and 'GcVisitor'. I just added
where Self: Sizedbounds to all the generic methods and added an object-safe fallback.This main problem is that 'Trace' and 'GcSafe' have associated
constants 'Trace::NEEDS_TRACE' and 'GcSafe::NEEDS_DROP'.
I've separated these associated consts into their own GcTypeInfno
trait.
I've messed around with this all day.
So far it looks like we might end up requiring significant amounts of
nightly features D:
Fixes #15
Required for #16
Goals
NullTracemaker as a simpler alternativestd::mem::needs_dropmay return a false-positivetrueStaticTypeLayoutstruct in the futureApproaches
I see three main approaches that keep the current type hierarchy, and there are pitfalls to each one.
Right now I'm leaning towards separate
DynTracetypes or having separate trait requirements for sized and unsized types.Require
where *const Self: GcTypeInfofor all Trace typesDirectly extending
TracefromGcTypeInfodoesn't work. The rust compiler requires supertypes to be object-safe.When wrapped with a pointer, the type can then implement non-object-safe constants. We simply add functions
needs_traceandneeds_gc_dropwhich query<*const T as GcTypeInfo>::NEEDS_TRACE(orNEEDS_DROP).The big advantage is this approach works on stable!
The disadvantage is that the bound isn't implied for generic parameters.
If you have
impl<T: Trace> Trace for Box<T>then the compiler complains that*const Tdoesn't implementGcTypeInfo.You must explicitly add a where-bound
where *const T: GcTypeInfoto each and every generic parameter 🙄The implied generic parameters would be fixed by RFC 2089. However, this would probably require nightly, maybe even for API clients.
Specialization
We could use specialization internally to implement
needs_traceandneeds_dropfunctions.Problems
min_specializationspecializationfeature is unsound, so we absolutely want to avoid it#[rustc_unsafe_specialization_marker]GcTypeInfoto be implemented for allTrace + Sizedtypes?Associated Type
We could require an associated type
type Info: GcTypeInfo. By default, the associated type would beSelffor anySizedtypes. However, for unsized types we would haveInfo = DefaultTypeInfothat gives conservative defaults.Associated type defaults were accepted in RFC 2532.
Problems
Self: GcTypeInfobound on the trait?Separate erased traits:
DynTraceandDynGcVisitorThis is what erased serde did. This is the first approach I tried. It was complex because I had to essentially make the GC accept both
TraceandDynTrace. They often needed to be treated separately and there was unessicarry code duplication :(Maybe we should make the
Tracetrait object safe by default, but keepGcSafesized?The main type that requires
GcSafeisGc<'gc, T>(for safety). That requirement could certainly be relaxed to only the constructors.......Could we have separate traits
GcSafeandGcStaticType? Any sized type must implementGcStaticTypeand give us the flags (NEEDS_TRACE/NEEDS_DROP) that we want.