blob: 30fe8d941b5e83b7faf66931d6692f444bb19a50 [file] [log] [blame]
/*
REQUIRED_ARGS: -checkaction=context -preview=dip1000
PERMUTE_ARGS: -O -g -inline
*/
void test8765()
{
string msg;
try
{
int a = 0;
assert(a);
}
catch (Throwable e)
{
// no-message -> assert expression
msg = e.msg;
}
assert(msg == "0 != true");
}
void test9255()
{
string file;
try
{
int x = 0;
assert(x);
}
catch (Throwable e)
{
file = e.file;
}
version(Windows)
assert(file && file == r"runnable\testassert.d");
else
assert(file && file == "runnable/testassert.d");
}
// https://issues.dlang.org/show_bug.cgi?id=20114
void test20114()
{
// Function call returning simple type
static int fun() {
static int i = 0;
assert(i++ == 0);
return 3;
}
const a = getMessage(assert(fun() == 4));
assert(a == "3 != 4");
// Function call returning complex type with opEquals
static struct S
{
bool opEquals(const int x) const
{
return false;
}
}
static S bar()
{
static int i = 0;
assert(i++ == 0);
return S.init;
}
const b = getMessage(assert(bar() == 4));
assert(b == "S() != 4");
// Non-call expression with side effects
int i = 0;
const c = getMessage(assert(++i == 0));
assert(c == "1 != 0");
}
version (DigitalMars) version (Win64) version = DMD_Win64;
void test20375() @safe
{
static struct RefCounted
{
// Force temporary through "impure" generator function
static RefCounted create() @trusted
{
__gshared int counter = 0;
return RefCounted(++counter > 0);
}
static int instances;
static int postblits;
this(bool) @safe
{
instances++;
}
this(this) @safe
{
instances++;
postblits++;
}
~this() @safe
{
// make the dtor non-nothrow (we are tracking clean-ups during AssertError unwinding)
if (postblits > 100)
throw new Exception("");
assert(instances > 0);
instances--;
}
bool opEquals(RefCounted) @safe
{
return true;
}
}
{
auto a = RefCounted.create();
RefCounted.instances++; // we're about to construct an instance below, increment manually
assert(a == RefCounted()); // both operands are pure expressions => no temporaries
}
assert(RefCounted.instances == 0);
assert(RefCounted.postblits == 0);
{
auto a = RefCounted.create();
assert(a == RefCounted.create()); // impure rhs is promoted to a temporary lvalue => copy for a.opEquals(rhs)
}
assert(RefCounted.instances == 0);
assert(RefCounted.postblits == 1);
RefCounted.postblits = 0;
{
const msg = getMessage(assert(RefCounted.create() != RefCounted.create())); // both operands promoted to temporary lvalues
assert(msg == "RefCounted() == RefCounted()");
}
version (DMD_Win64) // FIXME: temporaries apparently not destructed when unwinding via AssertError
{
assert(RefCounted.instances >= 0 && RefCounted.instances <= 2);
RefCounted.instances = 0;
}
else
assert(RefCounted.instances == 0);
assert(RefCounted.postblits == 1);
RefCounted.postblits = 0;
static int numGetLvalImpureCalls = 0;
ref RefCounted getLvalImpure() @trusted
{
numGetLvalImpureCalls++;
__gshared lval = RefCounted(); // not incrementing RefCounted.instances
return lval;
}
{
const msg = getMessage(assert(getLvalImpure() != getLvalImpure())); // both operands promoted to ref temporaries
assert(msg == "RefCounted() == RefCounted()");
}
assert(numGetLvalImpureCalls == 2);
assert(RefCounted.instances == 0);
assert(RefCounted.postblits == 1);
RefCounted.postblits = 0;
}
// https://issues.dlang.org/show_bug.cgi?id=21471
void test21471()
{
{
static struct S
{
S get()() const { return this; }
}
static auto f(S s) { return s; }
auto s = S();
assert(f(s.get) == s);
}
{
pragma(inline, true)
real exp(real x) pure nothrow
{
return x;
}
bool isClose(int lhs, real rhs) pure nothrow
{
return false;
}
auto c = 0;
assert(!isClose(c, exp(1)));
}
}
// https://issues.dlang.org/show_bug.cgi?id=20581
void test20581() @safe
{
static auto retro(return scope int[] s) @safe
{
static struct R
{
int[] source;
}
return R(s);
}
int[5] a = [ 1, 2, 3, 4, 5 ];
// Creates ref temporary __assertTmpXXXX for source
// Error: address of variable a assigned to __assertOp27 with longer lifetime
assert(retro(a[]).source is a[]);
}
string getMessage(T)(lazy T expr) @trusted
{
try
{
expr();
return null;
}
catch (Throwable t)
{
return t.msg;
}
}
void testMixinExpression() @safe
{
static struct S
{
bool opEquals(S) @safe { return true; }
}
const msg = getMessage(assert(mixin("S() != S()")));
assert(msg == "S() == S()");
}
void testUnaryFormat()
{
int zero = 0, one = 1;
assert(getMessage(assert(zero)) == "0 != true");
assert(getMessage(assert(!one)) == "1 == true");
assert(getMessage(assert(!cast(int) 1.5)) == "1 == true");
assert(getMessage(assert(!!__ctfe)) == "assert(__ctfe) failed!");
static struct S
{
int i;
bool opCast() const
{
return i < 2;
}
}
assert(getMessage(assert(S(4))) == "S(4) != true");
S s = S(4);
assert(getMessage(assert(*&s)) == "S(4) != true");
assert(getMessage(assert(--(++zero))) == "0 != true");
}
void testAssignments()
{
int a = 1;
int b = 2;
assert(getMessage(assert(a -= --b)) == "0 != true");
static ref int c()
{
static int counter;
counter++;
return counter;
}
assert(getMessage(assert(--c())) == "0 != true");
}
/// https://issues.dlang.org/show_bug.cgi?id=21472
void testTupleFormat()
{
alias AliasSeq(T...) = T;
// Default usage
{
alias a = AliasSeq!(1, "ab");
alias b = AliasSeq!(false, "xyz");
assert(getMessage(assert(a == b)) == `(1, "ab") != (false, "xyz")`);
}
// Single elements work but are not formatted as tuples
// Is this acceptable? (a workaround would probably require a
// different name for the tuple formatting hook)
{
alias a = AliasSeq!(1, "ab", []);
alias b = AliasSeq!(false, "xyz", [1]);
assert(getMessage(assert(a == b)) == `(1, "ab", []) != (false, "xyz", [1])`);
}
// Also works with tupleof (as taken from the bug report)
{
static struct Var { int a, b; }
const a = Var(1, 2);
const b = Var(3, 4);
const msg = getMessage(assert(a.tupleof == b.tupleof));
assert(msg == `(1, 2) != (3, 4)`);
}
// Also works when creating temporaries for the TupleExp
{
static struct S
{
int a, b;
}
static S get()
{
static int i;
return S(i++, i++);
}
auto a = get().tupleof;
auto b = get().tupleof;
string msg = getMessage(assert(a == b));
assert(msg == `(0, 1) != (2, 3)`);
msg = getMessage(assert(get().tupleof == AliasSeq!(2, 3)));
assert(msg == `(4, 5) != (2, 3)`);
msg = getMessage(assert(get().tupleof == get().tupleof));
assert(msg == `(6, 7) != (8, 9)`);
}
}
// https://issues.dlang.org/show_bug.cgi?id=21682
void testStaticOperators()
{
static class environment {
static bool opCmp(scope const(char)[] name)
{
return false;
}
static bool opBinaryRight(string op : "in")(scope const(char)[] name)
{
return false;
}
}
string msg = getMessage(assert(environment < "Hello"));
assert(msg == `"environment" >= "Hello"`);
msg = getMessage(assert("Hello" in environment));
assert(msg == `"Hello" !in "environment"`);
}
void main()
{
test8765();
test9255();
test20114();
test20375();
test21471();
test20581();
testMixinExpression();
testUnaryFormat();
testAssignments();
testTupleFormat();
testStaticOperators();
}