Proposal for types
Add the following type suffixes to the language: ?
and !
.
For every reference type (definition excludes slices, see below) T
, the meaning of T!
is “non-nullable T
” and the meaning of T?
is “nullable T
”, and T
without suffix means either T?
or T!
depending on context. For every non-reference type, T!
is a synonym for T
; there is T?
added for an optional type with the same values as T
plus a dedicated null
value.
Naturally, T!
converts to T?
implicitly, but for T?
to T!
, an explicit cast is required and that cast is @system
.
Multiple suffixes are allowed: T?!
is T!
and T!?
is T?
. That is, later !
or ?
override any previous ones.
Every lexical use of a reference type without ?
or !
appended is equivalent to one of them, depending on the module’s default. The module’s default is either specified (default null module m;
or default !null module m;
) or is the language’s default (which depends on the Edition).
In class member functions, this
has !
type.
Any operation that requires a value of type T?
to be non-null is a compile-time error.
Add operators for null-respecting access: ?.
, ?(…)
(call if not null), ?[…]
(index if not null), ?=
(assign if null).
For if (auto x = expr)
and if (T! x = expr)
if expr
is of type T?
, x
infers type T!
and contrary to normal variable definitions, in an if
or while
condition, T?
implicitly converts to T!
.
To wrap the rest of the function in the then block of such an if
statement, add if (auto x = expr) ... else …
to the language. The ...
is part of the core syntax and is intended to be read as “whatever follows next.” The else
branch is mandatory, and its …
means any statement or a possibly empty block. However, for an else
block that would be { bool f = false; assert(f); }
, add assert(auto x !is expr)
to the language. (Note that assert(0)
as special semantics and isn’t equivalent to a failed assertion.) Assert with declaration enforces non-null for a possibly null value.
No data flow analysis is proposed. Null checking is local and done by tracking ?
and !
by the type system.
Proposal for ref
The most difficult one is ref
. ref
parameters and variables are assumed to be non-null, i.e. for ref x
, &x
should not be null
. To allow for null references, add ref?
.
Non-null enforcement of ref
should be done even in the current edition to some degree. My bet is not a single D program ever correctly expected and handled a ref
returning function returning a null reference. One would have to take the address of the result and test that pointer for null. No-one does that, except some people toying around with the edges of the language intentionally used ref
with null.
In the current Edition, because T*
is T*?
, a dereferenced pointer is a possibly null reference. Binding one by ref
would be an error. For this special case, I propose to allow it instead, as some programs would be full of errors (or deprecation warnings) otherwise.
Reference types
In this DIP Idea, reference types are:
- Pointer types
- Class / interface types
- Associative array types
- Function pointer types
- Delegate types
Slice types are not reference types in this logic because null slices are equivalent to an empty slices for the most part.