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 }