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: Sized
bounds 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
NullTrace
maker as a simpler alternativestd::mem::needs_drop
may return a false-positivetrue
StaticTypeLayout
struct 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
DynTrace
types or having separate trait requirements for sized and unsized types.Require
where *const Self: GcTypeInfo
for all Trace typesDirectly extending
Trace
fromGcTypeInfo
doesn'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_trace
andneeds_gc_drop
which 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 T
doesn't implementGcTypeInfo
.You must explicitly add a where-bound
where *const T: GcTypeInfo
to 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_trace
andneeds_drop
functions.Problems
min_specialization
specialization
feature is unsound, so we absolutely want to avoid it#[rustc_unsafe_specialization_marker]
GcTypeInfo
to be implemented for allTrace + Sized
types?Associated Type
We could require an associated type
type Info: GcTypeInfo
. By default, the associated type would beSelf
for anySized
types. However, for unsized types we would haveInfo = DefaultTypeInfo
that gives conservative defaults.Associated type defaults were accepted in RFC 2532.
Problems
Self: GcTypeInfo
bound on the trait?Separate erased traits:
DynTrace
andDynGcVisitor
This 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
Trace
andDynTrace
. They often needed to be treated separately and there was unessicarry code duplication :(Maybe we should make the
Trace
trait object safe by default, but keepGcSafe
sized?The main type that requires
GcSafe
isGc<'gc, T>
(for safety). That requirement could certainly be relaxed to only the constructors.......Could we have separate traits
GcSafe
andGcStaticType
? Any sized type must implementGcStaticType
and give us the flags (NEEDS_TRACE/NEEDS_DROP) that we want.