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)() { return _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 70 if (isAssignable!(U, T)) 71 { 72 return NotNull!_value; 73 } 74 75 version(none) // NOTE: Disabled because it makes members inaccessible 76 { 77 /* See_Also: 78 * http://forum.dlang.org/thread/aprsozwvnpnchbaswjxd@forum.dlang.org#post-aprsozwvnpnchbaswjxd:40forum.dlang.org 79 */ 80 import std.traits: BaseClassesTuple; 81 static if (is(T == class) && !is(T == Object)) 82 { 83 @property NotNull!(BaseClassesTuple!T[0]) _valueHelper() inout @trusted pure nothrow 84 { 85 assert(_value !is null); // sanity check of invariant 86 return assumeNotNull(cast(BaseClassesTuple!T[0]) _value); 87 } 88 } 89 else 90 { 91 @property inout(T) _valueHelper() inout pure nothrow 92 { 93 assert(_value !is null); // sanity check of invariant 94 return _value; 95 } 96 } 97 } 98 99 // Apparently a compiler bug - the invariant being uncommented breaks all kinds of stuff. 100 // invariant() { assert(_value !is null); } 101 } 102 103 /** A convenience function to construct a NotNull value from something $(D t) 104 you know isn't null. 105 */ 106 NotNull!T assumeNotNull(T)(T t) 107 if (is(T == class) || 108 is(T == interface) || 109 is(T == U*, U) && __traits(isScalar, T)) 110 { 111 return NotNull!T(t); // note the constructor asserts it is not null 112 } 113 114 /** A convenience function to check for null $(D t). 115 116 If you pass null to $(D t), it will throw an exception. Otherwise, return 117 NotNull!T. 118 */ 119 NotNull!T enforceNotNull(T, string file = __FILE__, size_t line = __LINE__)(T t) 120 if (is(T == class) || 121 is(T == interface) || 122 is(T == U*, U) && __traits(isScalar, T)) 123 { 124 import std.exception: enforce; 125 enforce(t !is null, "t is null!", file, line); 126 return NotNull!T(t); 127 } 128 129 unittest 130 { 131 import core.exception; 132 import std.exception; 133 134 void NotNullCompilationTest1()() // I'm making these templates to defer compiling them 135 { 136 NotNull!(int*) defaultInitiliation; // should fail because this would be null otherwise 137 } 138 assert(!__traits(compiles, NotNullCompilationTest1!()())); 139 140 void NotNullCompiliationTest2()() 141 { 142 NotNull!(int*) defaultInitiliation = null; // should fail here too at compile time 143 } 144 assert(!__traits(compiles, NotNullCompiliationTest2!()())); 145 146 int dummy; 147 NotNull!(int*) foo = &dummy; 148 149 assert(!__traits(compiles, foo = null)); // again, literal null is caught at compile time 150 151 int* test; 152 153 test = &dummy; 154 155 foo = test.assumeNotNull; // should be fine 156 157 void bar(int* a) {} 158 159 // these should both compile, since NotNull!T is a subtype of T 160 bar(test); 161 bar(foo); 162 163 void takesNotNull(NotNull!(int*) a) { } 164 165 assert(!__traits(compiles, takesNotNull(test))); // should not work; plain int might be null 166 takesNotNull(foo); // should be fine 167 168 takesNotNull(test.assumeNotNull); // this should work too 169 assert(!__traits(compiles, takesNotNull(null.assumeNotNull))); // notNull(null) shouldn't compile 170 test = null; // reset our pointer 171 172 assertThrown!AssertError(takesNotNull(test.assumeNotNull)); // test is null now, so this should throw an assert failure 173 174 void takesConstNotNull(in NotNull!(int *) a) {} 175 176 test = &dummy; // make it valid again 177 takesConstNotNull(test.assumeNotNull); // should Just Work 178 179 NotNull!(int*) foo2 = foo; // we should be able to assign NotNull to other NotNulls too 180 foo2 = foo; // including init and assignment 181 182 } 183 184 unittest 185 { 186 class A {} 187 class B : A {} 188 NotNull!B b = (new B).assumeNotNull; 189 NotNull!A a = (new A).assumeNotNull; 190 assert(a && b); 191 a = b; 192 assert(a is b); 193 } 194 195 unittest 196 { 197 class A {} 198 class B : A {} 199 auto b = assumeNotNull(new B); 200 auto a = assumeNotNull(new A); 201 a = b; 202 assert(a is b); 203 } 204 205 /** See_Also: http://forum.dlang.org/thread/mxpfzghydhirdtltmmvo@forum.dlang.org?page=3#post-ngtuwqiqumommfrlngjy:40forum.dlang.org */ 206 unittest 207 { 208 class A {} 209 class B : A {} 210 void f(NotNull!A a) {} 211 NotNull!B b = assumeNotNull(new B); 212 static assert(!__traits(compiles, { f(b); })); // TODO: I don't want this to fail. 213 } 214 215 enum isNullInitializable(T) = __traits(compiles, { T _ = null; }); // currently unused 216 217 /// 218 @safe pure nothrow @nogc unittest 219 { 220 struct S {} 221 struct T { int* _; } 222 static assert( isNullInitializable!(int*)); 223 static assert(!isNullInitializable!(int)); 224 static assert(isNullInitializable!(string)); 225 static assert(!isNullInitializable!(float)); 226 static assert(!isNullInitializable!(S)); 227 static assert(!isNullInitializable!(T)); 228 }