Leaks could reasonably be considered unsafe as they can cause a program to crash due to resource exhausting, even where the actually used memory is limited.
If you do not consider leaks as part of the problem, there is a trivial way to avoid use-after-free and double-free: Simply never free any memory or only at the very end. (Which in some scenarios is exactly what people do.) The later would fulfill your definition of "holding references to be able to free them".
You can make this claim about any resource, there's no reason to single out memory here. You can run out of file descriptors, inodes, connections to a remote database, disk space, anything - including CPU time.
And notice that it wasn't the leak that you've now said was unsafe, it was the use itself. The program didn't blow up "because of a leak", it blew up because we exceeded some arbitrary resource threshold which may have been invisible to us.
> If you do not consider leaks as part of the problem, there is a trivial way to avoid use-after-free and double-free: Simply never free any memory
Indeed. And that's exactly what we see in some domains and if you've solved the other issues (e.g. bounds misses, type confusion) you've now got memory safe programs. Most general purpose software can't be written this way, but there is a whole heck of a lot of software out there which could be.
> The later would fulfill your definition of "holding references to be able to free them".
And that former would be characterized as "leak everything" and indeed that's entirely safe and, just as I said, to an end user this is a distinction which makes absolutely no difference.
To the end user memory leaks are not important as long as you do not exceed the available memory and the program does not crash. The moment it does, it a problem and it can be a risk. What I agree with, (if you argued that point - but you don't), is that it is more benign and qualitatively different risk than unbounded behavior you may get with an out-of-bounds access or use-after-free.
Are you claiming that there must be such transforms or only that it is likely that bugs exist which isn't interesting.
To assert that something specific always happens under UB is counter to the definition of UB. Fil-C carefully defines away UB for some operations, but to make full guarantee of safety, even by their definition and modulo bugs, I believe it necessary to fully remove UB.
Fil-C gets to look at some code which has UB if variable z is 9 and go "OK, lets check whether z is 9, and if so...". The result, of course, is much, much slower than executables from a typical modern C compiler, but since "It always does X" is in fact a permitted implementation of "Undefined Behaviour" this is a compliant C implementation, at least in most observable respects.