Open
Description
I would like to remove instances of unsafe in the code-base that aren't essential for performance or functionality. As per the contributor guidelines, this isn't a bug fix, so I'd like to ask what sort of changes would be acceptable in a PR before I do too much work. I've already done a bit just to see if it is even worthwhile, so I have some idea of what I might ask for:
- May I turn transitive dependencies into direct dependencies? See example 1 if this is unclear
- May I pull in small direct dependencies that Uiua does not currently transitively depend on? See example 3 for why.
- How much may I change to the structure of the code? I ask as I realize this is mostly a one person project, and changes to the structure of the code imposes a burden on you as a reviewer. See example 2 for the sort of thing I am thinking of.
- How important is debug mode performance? I fully intend to keep or improve release mode performance by checking that the llvm-ir or assembly does the same thing as the old unsafe code. (for structural changes at least) However, the debug mode code won't always work the same. See example 2 again for details. I can benchmark on the tests to make sure performance doesn't completely tank in debug mode, but I don't think all functionality and usage patterns are covered by the tests.
Examples of changes I've tried or want to try:
- bytemuck is directly or transitively used by 'cosmic-text', 'skrifra', 'arboard', and 'viuer'. All of these are optional dependencies either for features or for display to the terminal or graphical window. If I pull bytemuck in as a direct dependency, I can safely remove all but one instance of
mem::transmute
. This requires no structural changes to the code. - I can avoid the need to do a pointer cast from a
value::Value
to avalue::Repr
which is currently done inValue::shape
,Value::meta_mut
, and several similar functions. I can do this by moving the logic for the management ofarray::Array.meta
into a wrapper objectArrayMetaHolder
(to monomorphize the meta pointer management code), and then usingval_as_array!(self, |arr| &mut arr.meta).meta_mut()
and friends to get the shape and metadata from the array inside the value. I've checked the llvm-ir emitted by the rust compiler in release mode for several of the functions which inlineValue::meta_mut
and friends. I can confirm that in release mode the compiler realizes that checking the discriminant ofValue
is unnecessary and unconditionally offsets theValue
pointer to the location of theshape
ormeta
fields, which I assume was the point of castingValue
toRepr
in the first place. However, in debug mode what is generated is a switch on the discriminant to a bunch of identical blocks of code which all set the pointer value to the same thing. After the switch statementArrayMetaHolder::meta_mut
and friends are called if needed. - The last transmute bytemuck cannot eliminate is in
Array::convert
. It checks if we are converting to an array of the same type via TypeId, and transmutesArray<T>
toArray<U>
ifT == U
. This is essentially a specialization of theconvert
function whenT == U
. If I could pull in thecastaway
crate, I could replace this specialization with safe code. (To be clear, I haven't tried this yet) It also means that further specializations of generic functions for concrete types could be added to the codebase without needing to worry that much if the specialization is sound. See the discussion in the rust issue tracker about the 'min-specialization' and 'specialization' unstable features for why this sort of thing is tricky to do soundly in general. The castaway crate implements a known safe subset of specializations.
Metadata
Metadata
Assignees
Labels
No labels