thradams
Joined 4 years ago
Joined 4 years ago
"Owner pointers" must be uninitialized or null at the end of their scope.
Basically, the nullable state needs to be tracked at compile time, and nullable pointers,despite being a separate feature, reuse the same flow analysis.
For the impatient reader, a simplified way to think about it is to compare it with C++'s unique_ptr.
The difference is that, instead of runtime code being executed at the end of the scope (a destructor), we perform a compile-time check to ensure that the owner pointer is not referring to any object. The same before assignment.
So we get the same guarantees as C++ RAII, with some extras. In C++, the user has to adopt unique_ptr and additional wrappers (for example, for FILE). In this model, it works directly with malloc, fopen, etc., and is automatically safe, without the user having to opt in to "safety" or write wrappers. Safety is the default, and the safety requirements are propagated automatically.
It is interesting to note that propagation also works very well for struct members. Having an owner pointer as a struct member requires the user to provide a correct "destructor" or free the member manually before the struct object goes out of scope.
#pragma safety enable
#include <stdio.h>
int main() { FILE *_Owner _Opt f = fopen("file.txt", "r"); if (f) { fclose(f); } }
At the end of the scope of f, it can be in one of two possible states: "null" or "moved" (f is moved in the fclose call).
These are the expected states for an owner pointer at the end of its scope, so no warnings are issued.
Removing _Owner _Opt we have exactly the same code as users write today. But with the same or more guarantees than C++ RAII.
The C++ mutable keyword came to mind as a potential solution.
During the object creation (or destruction), the instance is considered to be in a transitional state, where the usual constraints—such as non-nullable pointers and immutability—are lifted. Once the transitional phase is over and the object is returned, the contract that governs the object (such as immutability of name and non-nullability of pointers) is fully reinstated.
This video shows how cake can help programmers to create safe code just fixing warnings.
We copy paste code then we add pragma safety enable
This enables two features ownership and nullable checks. Ownership will check if the fclose is called for instance, also checks double free etc, while nullable checks will check for de-referencing null pointers.
New qualifiers _Opt and _Owner are used but they can be empty macros, allowing the same code to be compiled without cake.
( The sample I have is 0, NULL and nullptr where nullptr is something new. Two ways of doing something makes the language complex. )
#include <ownership.h>
#include <stdio.h>
int main()
{
FILE *owner f = fopen("file.txt", "r");
if (f)
fclose(f);
}
But comparisons are inevitable, and I also think there are lessons learned
in Rust.C programmers uses contracts, these contracts are part of documentation of some API. For instance, if you call fopen you must call fclose.
All we need is to create contracts that the compiler can read and verify automatically.
Since then, I've significantly improved Cake's flow analysis.
The new analysis can track the origins of states across control-flow paths, making it easier to understand where a state came from and how it reached a particular point in the program.
Another milestone is that the entire Cake source code is now annotated with ownership and nullability, and checked by Cake itself.
I've documented the flow analysis here:
https://cakecc.org/flow.html