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 import std.traits: isAssignable; 31 32 @disable this(); // disallow default initialized (to null) 33 34 /** Assignment from $(D NotNull) inherited class $(D rhs) to $(D NotNull) base 35 class $(D this). */ 36 typeof(this) opAssign(U)(NotNull!U rhs) pure nothrow @safe @nogc if (isAssignable!(T, U)) { 37 this._value = rhs._value; 38 return this; 39 } 40 41 bool opCast(T : bool)() => _value !is null; 42 43 /// Constructs with a runtime not null check (via assert()). 44 this(T value) pure nothrow { 45 assert(value !is null); 46 _value = value; 47 } 48 49 /** Disable null construction. */ 50 @disable this(typeof(null)); 51 52 /** Disable null assignment. */ 53 @disable typeof(this) opAssign(typeof(null)); 54 55 @property inout(T) get() inout pure nothrow @safe @nogc { 56 assert(_value !is null); 57 return _value; 58 } 59 alias get this; /// this is substitutable for the regular (nullable) type 60 61 private T _value; 62 63 version (none): /+ TODO: activate with correct template restriction +/ 64 NotNull!U opCast(U)() pure nothrow if (isAssignable!(U, T)) => NotNull!_value; 65 66 version (none) { // NOTE: Disabled because it makes members inaccessible 67 /* See_Also: 68 * http://forum.dlang.org/thread/aprsozwvnpnchbaswjxd@forum.dlang.org#post-aprsozwvnpnchbaswjxd:40forum.dlang.org 69 */ 70 import std.traits: BaseClassesTuple; 71 static if (is(T == class) && !is(T == Object)) { 72 @property NotNull!(BaseClassesTuple!T[0]) _valueHelper() inout @trusted pure nothrow { 73 assert(_value !is null); // sanity check of invariant 74 return assumeNotNull(cast(BaseClassesTuple!T[0]) _value); 75 } 76 } else { 77 @property inout(T) _valueHelper() inout pure nothrow { 78 assert(_value !is null); // sanity check of invariant 79 return _value; 80 } 81 } 82 } 83 84 // Apparently a compiler bug - the invariant being uncommented breaks all kinds of stuff. 85 // invariant() { assert(_value !is null); } 86 } 87 88 /** A convenience function to construct a NotNull value from something $(D t) 89 you know isn't null. 90 */ 91 NotNull!T assumeNotNull(T)(T t) 92 if (is(T == class) || 93 is(T == interface) || 94 is(T == U*, U) && __traits(isScalar, T)) 95 => NotNull!T(t); // note the constructor asserts it is not null 96 97 /** A convenience function to check for null $(D t). 98 99 If you pass null to $(D t), it will throw an exception. Otherwise, return 100 NotNull!T. 101 */ 102 NotNull!T enforceNotNull(T, string file = __FILE__, size_t line = __LINE__)(T t) 103 if (is(T == class) || 104 is(T == interface) || 105 is(T == U*, U) && __traits(isScalar, T)) { 106 import std.exception: enforce; 107 enforce(t !is null, "t is null!", file, line); 108 return NotNull!T(t); 109 } 110 111 /// 112 unittest { 113 import core.exception; 114 import std.exception; 115 116 void NotNullCompilationTest1()() // I'm making these templates to defer compiling them 117 { 118 NotNull!(int*) defaultInitiliation; // should fail because this would be null otherwise 119 } 120 assert(!__traits(compiles, NotNullCompilationTest1!()())); 121 122 void NotNullCompiliationTest2()() { 123 NotNull!(int*) defaultInitiliation = null; // should fail here too at compile time 124 } 125 assert(!__traits(compiles, NotNullCompiliationTest2!()())); 126 127 int dummy; 128 NotNull!(int*) foo = &dummy; 129 130 assert(!__traits(compiles, foo = null)); // again, literal null is caught at compile time 131 132 int* test; 133 134 test = &dummy; 135 136 foo = test.assumeNotNull; // should be fine 137 138 void bar(int* a) {} 139 140 // these should both compile, since NotNull!T is a subtype of T 141 bar(test); 142 bar(foo); 143 144 void takesNotNull(NotNull!(int*) a) { } 145 146 assert(!__traits(compiles, takesNotNull(test))); // should not work; plain int might be null 147 takesNotNull(foo); // should be fine 148 149 takesNotNull(test.assumeNotNull); // this should work too 150 assert(!__traits(compiles, takesNotNull(null.assumeNotNull))); // notNull(null) shouldn't compile 151 test = null; // reset our pointer 152 153 assertThrown!AssertError(takesNotNull(test.assumeNotNull)); // test is null now, so this should throw an assert failure 154 155 void takesConstNotNull(in NotNull!(int *) a) {} 156 157 test = &dummy; // make it valid again 158 takesConstNotNull(test.assumeNotNull); // should Just Work 159 160 NotNull!(int*) foo2 = foo; // we should be able to assign NotNull to other NotNulls too 161 foo2 = foo; // including init and assignment 162 163 } 164 165 /// 166 unittest { 167 class A {} 168 class B : A {} 169 NotNull!B b = (new B).assumeNotNull; 170 NotNull!A a = (new A).assumeNotNull; 171 assert(a && b); 172 a = b; 173 assert(a is b); 174 } 175 176 /// 177 unittest { 178 class A {} 179 class B : A {} 180 auto b = assumeNotNull(new B); 181 auto a = assumeNotNull(new A); 182 a = b; 183 assert(a is b); 184 } 185 186 /** See_Also: http://forum.dlang.org/thread/mxpfzghydhirdtltmmvo@forum.dlang.org?page=3#post-ngtuwqiqumommfrlngjy:40forum.dlang.org */ 187 /// 188 unittest { 189 class A {} 190 class B : A {} 191 void f(NotNull!A a) {} 192 NotNull!B b = assumeNotNull(new B); 193 static assert(!__traits(compiles, { f(b); })); /+ TODO: I don't want this to fail. +/ 194 }