On Wednesday, 13 March 2024 at 06:05:35 UTC, Walter Bright wrote:
> Memory safety is not something we've uniquely defined. It's generally accepted that it specifically means no memory corruption. "Safe" programs can offer additional guarantees, like no race conditions.
Yeah, race condition is the second headache after memory safety (include null pointer issues) :)
I know only one language which give guarantee at compilation time about absence of race condition (but not deadlock). It is Rust.
But D have shared
keyword and as I understand it can provide such guarantee at compilation time for SafeD, right?
> In general, we cannot guarantee that assert()'s won't trip at runtime, nor buffer overflow exceptions. All we can do is say we stop the program when it happens.
As I understand there is only one case when D stop program - it is null pointer, correct?
So if D will support null safety it will be very reliable solution :)
> Consider the following:
I mean nullable types like in Kotlin (originally in Ceylon) and null safety model around it: https://kotlinlang.org/docs/null-safety.html
A? a; // <type name>? is nullable. The variable 'a' is implicity initialized by zero. Compiler allow this.
A b; // compilation error, because type 'A' (without question mark) is not nullable. So you should initialize it explicity.
A c = new A; // ok
a.run(); // compilation error, because variable 'a' can be null (type is nullable 'A?')
if (a != null) {
a.run(); // ok, because type of variable 'a' is A (not nullable) in this branch.
}
c.run() // ok, because type of variable 'c' is A (not nullable)
In your example (see comment after line void foo(int i) {
):
class A { void bar(); }
void foo(int i) {
A a; // compilation error, type A is not nullable
if (i) a = new A();
...
if (i) a.bar();
}
> What happens if we apply data flow analysis to determine the state of a
when it calls bar()
? It will determine that a
has the possible values (null
, new A()). Hence, it will give an error that
a` is possibly null at that point.
Yet the code is correct, not buggy.
Yes, the compiler could figure out that i
is the same, but the conditions can be more complex such that the compiler cannot figure it out (the halting problem).
So that doesn't work.
BTW, doing data flow analysis is very expensive in terms of compiler run time. The optimizer does it, but running the optimizer is optional for that reason.
The general strategy for compiler to allow or deny dereference is based on variable type. If variable is nullable type dereference is denied. If variable is not nullable type dereference is allowed. Variable type can be implicity changed by explicity check it value with null and will be not nullable type inside branch. Certainly variable should be effectively immutable in it life cycle scope.
Such strategy at comilation time is not very expensive and shouldn't depends on compiler optimizations.
Anyway I think developer can accept some delay of project build in exchange for reliable software :)
> We could lower a.bar()
to NullCheck(a).bar()
which throws an exception if a
is null. But what have we gained there? Nothing. The program still aborts with an exception, just like if the hardware checked. Except we've got this manual check that costs extra code and CPU time.
I agree that NullCheck(a).bar()
is not solution, because just moves issue in runtime.