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 }