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