Memory safe in D

Alex akornilov.82 at mail.ru
Wed Mar 13 07:43:23 UTC 2024


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
```d
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) {`):
```d
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.



More information about the Digitalmars-d mailing list