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 }