| /* |
| 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(); |
| } |