1 module nxt.notnull;
2 
3 /** An of a reference type `T` never being `null`.
4 
5    * Must be initialized when declared.
6 
7    * Can never be assigned the null literal (compile time error).
8 
9    * If assigned a `null` value at runtime an exception will be thrown.
10 
11    `NotNull!T` can be substituted for `T` at any time, but `T` cannot become
12    `NotNull` without some attention: either declaring `NotNull!T`, or using the
13    convenience function, `enforceNotNull!T`.
14 
15    Condition: `T` must be a reference type.
16 
17    Examples:
18    ---
19    int myInt;
20    NotNull!(int *) not_null = &myInt;
21    // you can now use variable not_null anywhere you would
22    // have used a regular int*, but with the assurance that
23    // it never stored null.
24    ---
25 */
26 struct NotNull(T)
27 if (is(T == class) ||
28     is(T == interface) ||
29     is(T == U*, U) && __traits(isScalar, T))
30 {
31     import std.traits: isAssignable;
32 
33     @disable this(); // disallow default initialized (to null)
34 
35     /** Assignment from $(D NotNull) inherited class $(D rhs) to $(D NotNull) base
36         class $(D this). */
37     typeof(this) opAssign(U)(NotNull!U rhs) @safe pure nothrow @nogc
38     if (isAssignable!(T, U))
39     {
40         this._value = rhs._value;
41         return this;
42     }
43 
44     bool opCast(T : bool)() => _value !is null;
45 
46     /// Constructs with a runtime not null check (via assert()).
47     this(T value) pure nothrow
48     {
49         assert(value !is null);
50         _value = value;
51     }
52 
53     /** Disable null construction. */
54     @disable this(typeof(null));
55 
56     /** Disable null assignment. */
57     @disable typeof(this) opAssign(typeof(null));
58 
59     @property inout(T) get() inout @safe pure nothrow @nogc
60     {
61         assert(_value !is null);
62         return _value;
63     }
64     alias get this; /// this is substitutable for the regular (nullable) type
65 
66     private T _value;
67 
68     version(none):           // TODO: activate with correct template restriction
69         NotNull!U opCast(U)() pure nothrow if (isAssignable!(U, T)) => NotNull!_value;
70 
71     version(none)        // NOTE: Disabled because it makes members inaccessible
72     {
73         /* See_Also:
74          * http://forum.dlang.org/thread/aprsozwvnpnchbaswjxd@forum.dlang.org#post-aprsozwvnpnchbaswjxd:40forum.dlang.org
75          */
76         import std.traits: BaseClassesTuple;
77         static if (is(T == class) && !is(T == Object))
78         {
79             @property NotNull!(BaseClassesTuple!T[0]) _valueHelper() inout @trusted pure nothrow
80             {
81                 assert(_value !is null); // sanity check of invariant
82                 return assumeNotNull(cast(BaseClassesTuple!T[0]) _value);
83             }
84         }
85         else
86         {
87             @property inout(T) _valueHelper() inout pure nothrow
88             {
89                 assert(_value !is null); // sanity check of invariant
90                 return _value;
91             }
92         }
93     }
94 
95     // Apparently a compiler bug - the invariant being uncommented breaks all kinds of stuff.
96     // invariant() { assert(_value !is null); }
97 }
98 
99 /** A convenience function to construct a NotNull value from something $(D t)
100     you know isn't null.
101 */
102 NotNull!T assumeNotNull(T)(T t)
103 if (is(T == class) ||
104     is(T == interface) ||
105     is(T == U*, U) && __traits(isScalar, T))
106 	=> NotNull!T(t); // note the constructor asserts it is not null
107 
108 /** A convenience function to check for null $(D t).
109 
110     If you pass null to $(D t), it will throw an exception. Otherwise, return
111     NotNull!T.
112 */
113 NotNull!T enforceNotNull(T, string file = __FILE__, size_t line = __LINE__)(T t)
114 if (is(T == class) ||
115     is(T == interface) ||
116     is(T == U*, U) && __traits(isScalar, T))
117 {
118     import std.exception: enforce;
119     enforce(t !is null, "t is null!", file, line);
120     return NotNull!T(t);
121 }
122 
123 unittest
124 {
125     import core.exception;
126     import std.exception;
127 
128     void NotNullCompilationTest1()() // I'm making these templates to defer compiling them
129     {
130         NotNull!(int*) defaultInitiliation; // should fail because this would be null otherwise
131     }
132     assert(!__traits(compiles, NotNullCompilationTest1!()()));
133 
134     void NotNullCompiliationTest2()()
135     {
136         NotNull!(int*) defaultInitiliation = null; // should fail here too at compile time
137     }
138     assert(!__traits(compiles, NotNullCompiliationTest2!()()));
139 
140     int dummy;
141     NotNull!(int*) foo = &dummy;
142 
143     assert(!__traits(compiles, foo = null)); // again, literal null is caught at compile time
144 
145     int* test;
146 
147     test = &dummy;
148 
149     foo = test.assumeNotNull; // should be fine
150 
151     void bar(int* a) {}
152 
153     // these should both compile, since NotNull!T is a subtype of T
154     bar(test);
155     bar(foo);
156 
157     void takesNotNull(NotNull!(int*) a) { }
158 
159     assert(!__traits(compiles, takesNotNull(test))); // should not work; plain int might be null
160     takesNotNull(foo); // should be fine
161 
162     takesNotNull(test.assumeNotNull); // this should work too
163     assert(!__traits(compiles, takesNotNull(null.assumeNotNull))); // notNull(null) shouldn't compile
164     test = null; // reset our pointer
165 
166     assertThrown!AssertError(takesNotNull(test.assumeNotNull)); // test is null now, so this should throw an assert failure
167 
168     void takesConstNotNull(in NotNull!(int *) a) {}
169 
170     test = &dummy; // make it valid again
171     takesConstNotNull(test.assumeNotNull); // should Just Work
172 
173     NotNull!(int*) foo2 = foo; // we should be able to assign NotNull to other NotNulls too
174     foo2 = foo; // including init and assignment
175 
176 }
177 
178 unittest
179 {
180     class A {}
181     class B : A {}
182     NotNull!B b = (new B).assumeNotNull;
183     NotNull!A a = (new A).assumeNotNull;
184     assert(a && b);
185     a = b;
186     assert(a is b);
187 }
188 
189 unittest
190 {
191     class A {}
192     class B : A {}
193     auto b = assumeNotNull(new B);
194     auto a = assumeNotNull(new A);
195     a = b;
196     assert(a is b);
197 }
198 
199 /** See_Also: http://forum.dlang.org/thread/mxpfzghydhirdtltmmvo@forum.dlang.org?page=3#post-ngtuwqiqumommfrlngjy:40forum.dlang.org */
200 unittest
201 {
202     class A {}
203     class B : A {}
204     void f(NotNull!A a) {}
205     NotNull!B b = assumeNotNull(new B);
206     static assert(!__traits(compiles, { f(b); })); // TODO: I don't want this to fail.
207 }
208 
209 enum isNullInitializable(T) = __traits(compiles, { T _ = null; }); // currently unused
210 
211 ///
212 @safe pure nothrow @nogc unittest
213 {
214     struct S {}
215     struct T { int* _; }
216     static assert( isNullInitializable!(int*));
217     static assert(!isNullInitializable!(int));
218     static assert(isNullInitializable!(string));
219     static assert(!isNullInitializable!(float));
220     static assert(!isNullInitializable!(S));
221     static assert(!isNullInitializable!(T));
222 }