As part of the initial set of proposals for Span, RawSpan, MutableSpan and others, we accepted some functions that reinterpret typed memory as untyped bytes. Some of these have since then had to be revised with an @unsafe annotation. As a result, their names do not reflect the unsafety in typical use (when compiled without -strict-memory-safety). This discussion is about how to solve this discrepancy.
The underlying issue is that after LLVM initializes some storage with a value, it may choose to treat any padding bytes within the storage of the value as uninitialized. This in keeping with its identity as a C compiler, but is neither the only possible choice nor necessarily a desirable behaviour from the compiler for languages that use LLVM but are not C, such as Swift.
We can solve the naming discrepancy in one of two ways:
Change LLVM's treatment of padding bytes (in Swift mode) so that they do not lead to undefined behaviour, and then remove the @unsafe annotations for these functions or properties.
Rename these functions and properties to reflect that they are unsafe.
The second option is the simpler one, but doesn't solve the underlying issue. The unsafety comes from a decision of the C language that Swift might not make; is it feasible to make a different decision?
In the meantime, I have prepared a proposal document for renaming (PR), since the standard library is the part of the system I understand best. In short, the @unsafe symbols to be renamed are as follows:
I'd be glad for this proposal to be put aside in favor of changing LLVM. While the amount of effort is not the same, both have a similar time horizon for completion because of the lengthy deprecation window we must allow for.
Nit regarding the latter two operations: is it the append which is unsafe or the bytes being read from? If the former, the name really should be unsafeAppend.
Safety is always better than unsafety of course. If we rename now to reflect the currently unsafe reality but then make LLVM better, the newly-made-safe-previously-deprecated APIs can be straightforwardly undeprecated, presumably. So it doesn't seem either-or.
What are the downsides to changing LLVM's behavior (if any)?
Because honestly, to me as a "user" of the language, this reads:
Option A:
Deal with deprecations, migration work for everyone, a messy transition phase, and more unsafe littered all over codebases (for the shiny new "safe" language addition mind you), - or -
Option B:
just make the problem go away without any changes
so, why would anyone NOT want the LLVM adjustments?
Would changing LLVM behavior as described incur any performance impact to initialize the padding bytes? If so IMO it sounds questionable to do extra real runtime work for the purpose of avoiding uninitialized memory in the padding bytes of a struct, since interpreting a struct instance as raw bytes is uncommon and generally dangerous (not using the strict definition of that word) to begin with.
(Hand-waving follows… and since I might not have retained an earlier discussion correctly, it's probably wrong.) The idea would be to mark the state of such bytes so that if they're read once, they will keep providing the same value if they're read again. This doesn't require actually writing to them, but it's a change in the internal housekeeping of the compiler. Currently if the compiler has decided not to write a padding byte and marks it undefined instead, then trying to read that byte later might as well be from a random-number generator. If still in a scope where the compiler is still keeping track of that byte.
I thought this point was specifically raised during the evolution discussion, and there was a distinction drawn between the domains of safety that the @unsafe attribute and the unsafe API prefix relate to.
Pushing further on the idea of what happens if LLVM does get fixed, it's interesting to note that while hazards from undefined behaviour would be eliminated, there would remain a hazard from bytes whose value has not been intentionally set by the running program. Perhaps then a name should point to that hazard rather than to memory unsafety. The memory unsafety is already indicated by @unsafe, so maybe we can find a hazard-word about reading padding bytes.
unsafeBytes: this is the obvious starting point, but overstates the case once LLVM is fixed.
paddedBytes: similarly compact to unsafeBytes.
unspecifiedBytes: seems to refer to all the bytes.
bytesWithArbitraryPadding: quite explicit, but long.
bytesWithUnspecifiedPadding: see above
sparseBytes: not bad, but suggests a low signal-to-noise ratio. Independently suggested by two different colleagues.
These would be for the read direction; the read-write case of mutableBytes should probably keep "unsafe" in its new name.
Well, they're always bytes, right? They just might be...bitey?
I like this one not simply due to compactness but because it doesn't sound too absurd for padding of zero (at least to me, the verb in this context implies that nothing will be padded if there's no need for it--à la string padding). If we want to be wordy we could call it arbitrarilyPaddedBytes. I think the names that say the bytes come "with padding" are maybe a bit strong for that case.