r/Compilers • u/morglod • 3d ago
nullptr, zero initialization, is zero checks design question
Hi,
I'm working on mox (low level + metaprogramming) and currently in mox I have zero initialization for everything. For example:
x: T = 0;
x: []T = 0;
x: [10]T = 0;
Zero initializes the value. If T is struct, all fields gets zeroed, if it is fixed array, all elements are zeroed, if it is slice, it has ptr=0, len=0.
But the problem and question is with pointers. At first I thought adding explicit type for nullable pointers is a good thing. Because then you have explicit and readable contract on function arguments and you possibly could have some optimizations or checks in compiler. Because of this, not nullable pointer could not be initialized with zero (because it is semantically wrong).
But while I was writing in C++ and in mox I found that I never ever used nullable pointers. And not able to zero any pointer type just makes more headache. Having nil like in Odin instead of 0 makes not much sense for me. You just write more letters and think more about some special nil while 0 clearly says that you zero the value.
nullptr problem currently solved by having nullptr function:
fn foo(x: *i32) {}
fn foo(x: i32) {}
foo(nullptr(i32));
And in case we need zeroed value with expression, I have zeroed function:
fn foo(x: Smth) {}
foo(zeroed(Smth));
So the question is, what do you think about having explicit nil for zeroing and nullable pointers? How often do you really use it? Are there any other problems/things that I dont see here?
3
u/brat3108 2d ago
So the question is, what do you think about having explicit nil
I use 'nil' as a null value that works for all pointer types. (Probably I took it from Pascal.)
Its actual value, intepreted as bits, is understood to be all-zeros, but having a distinct type separate from an integer to me is useful.
How often do you really use it?
All the time. (I've just done a survey and it averages about once per 100 lines of code.)
Are there any other problems/things that I dont see here?
Not really; if it works, it works. This is not C where the language has to assume some bizarre hardware where a NULL pointer might not be all-bits-zero.
Although it looks odd initialising aggregate types with a single zero too.
For aggregates, I tried using the special expression empty:
A = empty
But in the end I made it a statement:
clear A
1
u/WittyStick 3d ago edited 3d ago
You probably want both nullable and non-nullable pointer types, with the subtyping relation
non-nullable <: nullable
Any non-nullable pointer can implicitly be used where a nullable one is expected, but where we expect a non-nullable pointer, we can't use an arbitrary nullable pointer - we must downcast it to a non-nullable one.
This downcast can be made implicit using Definite Assignment Analysis. If we've previously set a nullable variable to a non-null value, we can treat it as a non-null type. Eg, suppose we have ?i32 indicating a nullable pointer and !i32 indicating a non-nullable pointer.
x: !i32 = 0; // compiler error - can't assign null to non-nullable type.
y: ?i32 = 0; // this is fine.
fn foo(x: !i32) { }
foo(y); // compiler error - y is nullable but parameter is not-null.
if (y != null)
foo(y); // not a compiler error because DAA has proved y cannot be null here.
else
foo(y); // obviously a compiler error. DAA knows y is null.
a: !i32 = 1;
b: ?i32 = x; // implicit upcast to nullable
foo(b); // not a compiler error - DAA knows y is definitely assigned.
1
u/ZachVorhies 2d ago
I don’t get it
If you have no null pointers then you don’t have default construction
End of story
3
u/yuehuang 3d ago
I added
= defaultto mine. This applied to non-value types like structs as well. Sometimes enum don't start at 0.