Skip to content

Commit

Permalink
Merge pull request #1103 from monarchdodra/Nullable
Browse files Browse the repository at this point in the history
Fix Nullalble to throw logic errors instead of exceptions
  • Loading branch information
alexrp committed Feb 10, 2013
2 parents cac8c67 + 75f143a commit b314344
Showing 1 changed file with 178 additions and 51 deletions.
229 changes: 178 additions & 51 deletions std/typecons.d
Original file line number Diff line number Diff line change
Expand Up @@ -1154,13 +1154,6 @@ Forces $(D this) to the null state.
_isNull = true;
}

//@@@BUG4424@@@
private mixin template _workaround4424()
{
@disable void opAssign(ref const Nullable);
}
mixin _workaround4424;

/**
Assigns $(D value) to the internally-held state. If the assignment
succeeds, $(D this) becomes non-null.
Expand All @@ -1172,19 +1165,19 @@ succeeds, $(D this) becomes non-null.
}

/**
Gets the value. Throws an exception if $(D this) is in the null
state. This function is also called for the implicit conversion to $(D
T).
Gets the value. $(D this) must not be in the null state.
This function is also called for the implicit conversion to $(D T).
*/
@property ref inout(T) get() inout pure @safe
@property ref inout(T) get() inout pure nothrow @safe
{
enforce(!isNull);
enum message = "Called `get' on null Nullable!" ~ T.stringof ~ ".";
assert(!isNull, message);
return _value;
}

/**
Implicitly converts to $(D T). Throws an exception if $(D this) is in
the null state.
Implicitly converts to $(D T).
$(D this) must not be in the null state.
*/
alias get this;
}
Expand All @@ -1193,7 +1186,7 @@ unittest
{
Nullable!int a;
assert(a.isNull);
assertThrown(a.get);
assertThrown!Throwable(a.get);
a = 5;
assert(!a.isNull);
assert(a == 5);
Expand All @@ -1208,7 +1201,7 @@ unittest
a = a;
assert(a == 18);
a.nullify();
assertThrown(a += 2);
assertThrown!Throwable(a += 2);
}
unittest
{
Expand Down Expand Up @@ -1241,7 +1234,7 @@ unittest
s.x = 9190;
assert(s.x == 9190);
s.nullify();
assertThrown(s.x = 9441);
assertThrown!Throwable(s.x = 9441);
}
unittest
{
Expand All @@ -1252,7 +1245,7 @@ unittest
assert(n.isNull);
n = 4;
assert(!n.isNull);
try { assert(n == 4); } catch (Exception) { assert(false); }
assert(n == 4);
n.nullify();
assert(n.isNull);
}();
Expand Down Expand Up @@ -1287,6 +1280,91 @@ unittest
N n;
foo(n);
}
unittest
{
//Check nullable immutable is constructable
{
auto a1 = Nullable!(immutable int)();
auto a2 = Nullable!(immutable int)(1);
auto i = a2.get;
}
//Check immutable nullable is constructable
{
auto a1 = immutable (Nullable!int)();
auto a2 = immutable (Nullable!int)(1);
auto i = a2.get;
}
}
unittest
{
alias NInt = Nullable!int;

//Construct tests
{
//from other Nullable null
NInt a1;
NInt b1 = a1;
assert(b1.isNull);

//from other Nullable non-null
NInt a2 = NInt(1);
NInt b2 = a2;
assert(b2 == 1);

//Construct from similar nullable
auto a3 = immutable(NInt)();
NInt b3 = a3;
assert(b3.isNull);
}

//Assign tests
{
//from other Nullable null
NInt a1;
NInt b1;
b1 = a1;
assert(b1.isNull);

//from other Nullable non-null
NInt a2 = NInt(1);
NInt b2;
b2 = a2;
assert(b2 == 1);

//Construct from similar nullable
auto a3 = immutable(NInt)();
NInt b3 = a3;
b3 = a3;
assert(b3.isNull);
}
}
unittest
{
//Check nullable is nicelly embedable in a struct
static struct S1
{
Nullable!int ni;
}
static struct S2 //inspired from 9404
{
Nullable!int ni;
this(S2 other)
{
ni = other.ni;
}
void opAssign(S2 other)
{
ni = other.ni;
}
}
foreach (S; TypeTuple!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}

/**
Just like $(D Nullable!T), except that the null state is defined as a
Expand Down Expand Up @@ -1325,27 +1403,29 @@ Forces $(D this) to the null state.

/**
Assigns $(D value) to the internally-held state. No null checks are
made.
made. Note that the assignment may leave $(D this) in the null state.
*/
void opAssign()(T value)
{
_value = value;
}

/**
Gets the value. Throws an exception if $(D this) is in the null
state. This function is also called for the implicit conversion to $(D
T).
Gets the value. $(D this) must not be in the null state.
This function is also called for the implicit conversion to $(D T).
*/
@property ref inout(T) get()() inout
{
enforce(!isNull);
//@@@6169@@@: We avoid any call that might evaluate nullValue's %s,
//Because it might messup get's purity and safety inference.
enum message = "Called `get' on null Nullable!(" ~ T.stringof ~ ",nullValue).";
assert(!isNull, message);
return _value;
}

/**
Implicitly converts to $(D T). Throws an exception if $(D this) is in
the null state.
Implicitly converts to $(D T).
Gets the value. $(D this) must not be in the null state.
*/
alias get this;
}
Expand All @@ -1354,7 +1434,7 @@ unittest
{
Nullable!(int, int.min) a;
assert(a.isNull);
assertThrown(a.get);
assertThrown!Throwable(a.get);
a = 5;
assert(!a.isNull);
assert(a == 5);
Expand Down Expand Up @@ -1385,12 +1465,10 @@ unittest
function() pure nothrow @safe
{
Nullable!(int, int.min) n;
pragma(msg, typeof(&n.get!()));

assert(n.isNull);
n = 4;
assert(!n.isNull);
try { assert(n == 4); } catch (Exception) { assert(false); }
assert(n == 4);
n.nullify();
assert(n.isNull);
}();
Expand All @@ -1412,6 +1490,33 @@ unittest
s.nullify();
assert(s.isNull);
}
unittest
{
//Check nullable is nicelly embedable in a struct
static struct S1
{
Nullable!(int, 0) ni;
}
static struct S2 //inspired from 9404
{
Nullable!(int, 0) ni;
this(S2 other)
{
ni = other.ni;
}
void opAssign(S2 other)
{
ni = other.ni;
}
}
foreach (S; TypeTuple!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}

/**
Just like $(D Nullable!T), except that the object refers to a value
Expand All @@ -1426,15 +1531,15 @@ struct NullableRef(T)
/**
Constructor binding $(D this) with $(D value).
*/
this(T * value) pure nothrow @safe
this(T* value) pure nothrow @safe
{
_value = value;
}

/**
Binds the internal state to $(D value).
*/
void bind(T * value) pure nothrow @safe
void bind(T* value) pure nothrow @safe
{
_value = value;
}
Expand All @@ -1459,25 +1564,27 @@ Forces $(D this) to the null state.
Assigns $(D value) to the internally-held state.
*/
void opAssign()(T value)
if (isAssignable!T) //@@@9416@@@
{
enforce(_value);
enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ ".";
assert(!isNull, message);
*_value = value;
}

/**
Gets the value. Throws an exception if $(D this) is in the null
state. This function is also called for the implicit conversion to $(D
T).
Gets the value. $(D this) must not be in the null state.
This function is also called for the implicit conversion to $(D T).
*/
@property ref inout(T) get()() inout
@property ref inout(T) get() inout pure nothrow @safe
{
enforce(!isNull);
enum message = "Called `get' on null NullableRef!" ~ T.stringof ~ ".";
assert(!isNull, message);
return *_value;
}

/**
Implicitly converts to $(D T). Throws an exception if $(D this) is in
the null state.
Implicitly converts to $(D T).
$(D this) must not be in the null state.
*/
alias get this;
}
Expand All @@ -1496,8 +1603,8 @@ unittest
a.nullify();
assert(x == 42);
assert(a.isNull);
assertThrown(a.get);
assertThrown(a = 71);
assertThrown!Throwable(a.get);
assertThrown!Throwable(a = 71);
a.bind(&y);
assert(a == 7);
y = 135;
Expand Down Expand Up @@ -1525,16 +1632,9 @@ unittest
assert(n.isNull);
n.bind(storage);
assert(!n.isNull);
try
{
assert(n == 19902);
n = 2294;
assert(n == 2294);
}
catch (Exception)
{
assert(false);
}
assert(n == 19902);
n = 2294;
assert(n == 2294);
assert(*storage == 2294);
n.nullify();
assert(n.isNull);
Expand All @@ -1560,6 +1660,33 @@ unittest
s.nullify();
assert(s.isNull);
}
unittest
{
//Check nullable is nicelly embedable in a struct
static struct S1
{
NullableRef!int ni;
}
static struct S2 //inspired from 9404
{
NullableRef!int ni;
this(S2 other)
{
ni = other.ni;
}
void opAssign(S2 other)
{
ni = other.ni;
}
}
foreach (S; TypeTuple!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}

/**
$(D BlackHole!Base) is a subclass of $(D Base) which automatically implements
Expand Down

0 comments on commit b314344

Please sign in to comment.