// PERMUTE_ARGS: -inline -O -property // REQUIRED_ARGS: -dip25 // Test operator overloading extern (C) int printf(const(char*) fmt, ...); template Seq(T...){ alias T Seq; } bool thrown(E, T)(lazy T val) { try { val(); return false; } catch (E e) { return true; } } void stompStack() { int[256] sa = 0xdeadbeef; } /**************************************/ class A { string opUnary(string s)() { printf("A.opUnary!(%.*s)\n", s.length, s.ptr); return s; } } void test1() { auto a = new A(); +a; -a; ~a; *a; ++a; --a; auto x = a++; assert(x == a); auto y = a--; assert(y == a); } /**************************************/ class A2 { T opCast(T)() { auto s = T.stringof; printf("A.opCast!(%.*s)\n", s.length, s.ptr); return T.init; } } void test2() { auto a = new A2(); auto x = cast(int)a; assert(x == 0); auto y = cast(char)a; assert(y == char.init); } /**************************************/ struct A3 { int opBinary(string s)(int i) { printf("A.opBinary!(%.*s)\n", s.length, s.ptr); return 0; } int opBinaryRight(string s)(int i) if (s == "/" || s == "*") { printf("A.opBinaryRight!(%.*s)\n", s.length, s.ptr); return 0; } T opCast(T)() { auto s = T.stringof; printf("A.opCast!(%.*s)\n", s.length, s.ptr); return T.init; } } void test3() { A3 a; a + 3; 4 * a; 4 / a; a & 5; } /**************************************/ struct A4 { int opUnary(string s)() { printf("A.opUnary!(%.*s)\n", s.length, s.ptr); return 0; } T opCast(T)() { auto s = T.stringof; printf("A.opCast!(%.*s)\n", s.length, s.ptr); return T.init; } } void test4() { A4 a; if (a) int x = 3; if (!a) int x = 3; if (!!a) int x = 3; } /**************************************/ class A5 { override bool opEquals(Object o) { printf("A.opEquals!(%p)\n", o); return 1; } int opUnary(string s)() { printf("A.opUnary!(%.*s)\n", s.length, s.ptr); return 0; } T opCast(T)() { auto s = T.stringof; printf("A.opCast!(%.*s)\n", s.length, s.ptr); return T.init; } } class B5 : A5 { override bool opEquals(Object o) { printf("B.opEquals!(%p)\n", o); return 1; } } void test5() { A5 a = new A5(); A5 a2 = new A5(); B5 b = new B5(); A n = null; if (a == a) int x = 3; if (a == a2) int x = 3; if (a == b) int x = 3; if (a == n) int x = 3; if (n == a) int x = 3; if (n == n) int x = 3; } /**************************************/ struct S6 { const bool opEquals(ref const S6 b) { printf("S.opEquals(S %p)\n", &b); return true; } const bool opEquals(ref const T6 b) { printf("S.opEquals(T %p)\n", &b); return true; } } struct T6 { const bool opEquals(ref const T6 b) { printf("T.opEquals(T %p)\n", &b); return true; } /+ const bool opEquals(ref const S6 b) { printf("T.opEquals(S %p)\n", &b); return true; } +/ } void test6() { S6 s1; S6 s2; if (s1 == s2) int x = 3; T6 t; if (s1 == t) int x = 3; if (t == s2) int x = 3; } /**************************************/ struct S7 { const int opCmp(ref const S7 b) { printf("S.opCmp(S %p)\n", &b); return -1; } const int opCmp(ref const T7 b) { printf("S.opCmp(T %p)\n", &b); return -1; } } struct T7 { const int opCmp(ref const T7 b) { printf("T.opCmp(T %p)\n", &b); return -1; } /+ const int opCmp(ref const S7 b) { printf("T.opCmp(S %p)\n", &b); return -1; } +/ } void test7() { S7 s1; S7 s2; if (s1 < s2) int x = 3; T7 t; if (s1 < t) int x = 3; if (t < s2) int x = 3; } /**************************************/ struct A8 { int opUnary(string s)() { printf("A.opUnary!(%.*s)\n", s.length, s.ptr); return 0; } int opIndexUnary(string s, T)(T i) { printf("A.opIndexUnary!(%.*s)(%d)\n", s.length, s.ptr, i); return 0; } int opIndexUnary(string s, T)(T i, T j) { printf("A.opIndexUnary!(%.*s)(%d, %d)\n", s.length, s.ptr, i, j); return 0; } int opSliceUnary(string s)() { printf("A.opSliceUnary!(%.*s)()\n", s.length, s.ptr); return 0; } int opSliceUnary(string s, T)(T i, T j) { printf("A.opSliceUnary!(%.*s)(%d, %d)\n", s.length, s.ptr, i, j); return 0; } } void test8() { A8 a; -a; -a[3]; -a[3, 4]; -a[]; -a[5 .. 6]; --a[3]; } /**************************************/ struct A9 { int opOpAssign(string s)(int i) { printf("A.opOpAssign!(%.*s)\n", s.length, s.ptr); return 0; } int opIndexOpAssign(string s, T)(int v, T i) { printf("A.opIndexOpAssign!(%.*s)(%d, %d)\n", s.length, s.ptr, v, i); return 0; } int opIndexOpAssign(string s, T)(int v, T i, T j) { printf("A.opIndexOpAssign!(%.*s)(%d, %d, %d)\n", s.length, s.ptr, v, i, j); return 0; } int opSliceOpAssign(string s)(int v) { printf("A.opSliceOpAssign!(%.*s)(%d)\n", s.length, s.ptr, v); return 0; } int opSliceOpAssign(string s, T)(int v, T i, T j) { printf("A.opSliceOpAssign!(%.*s)(%d, %d, %d)\n", s.length, s.ptr, v, i, j); return 0; } } void test9() { A9 a; a += 8; a -= 8; a *= 8; a /= 8; a %= 8; a &= 8; a |= 8; a ^= 8; a <<= 8; a >>= 8; a >>>= 8; a ~= 8; a ^^= 8; a[3] += 8; a[3] -= 8; a[3] *= 8; a[3] /= 8; a[3] %= 8; a[3] &= 8; a[3] |= 8; a[3] ^= 8; a[3] <<= 8; a[3] >>= 8; a[3] >>>= 8; a[3] ~= 8; a[3] ^^= 8; a[3, 4] += 8; a[] += 8; a[5 .. 6] += 8; } /**************************************/ struct BigInt { int opEquals(T)(T n) const { return 1; } int opEquals(T:int)(T n) const { return 1; } int opEquals(T:const(BigInt))(T n) const { return 1; } } int decimal(BigInt b, const BigInt c) { while (b != c) { } return 1; } /**************************************/ struct Foo10 { int opUnary(string op)() { return 1; } } void test10() { Foo10 foo; foo++; } /**************************************/ struct S4913 { bool opCast(T : bool)() { return true; } } int bug4913() { if (S4913 s = S4913()) { return 83; } return 9; } static assert(bug4913() == 83); /**************************************/ // 5551 struct Foo11 { Foo11 opUnary(string op:"++")() { return this; } Foo11 opBinary(string op)(int y) { return this; } } void test11() { auto f = Foo11(); f++; } /**************************************/ // 4099 struct X4099 { int x; alias x this; typeof(this) opUnary (string operator) () { printf("operator called\n"); return this; } } void test4099() { X4099 x; X4099 r1 = ++x; //operator called X4099 r2 = x++; //BUG! (alias this used. returns int) } /**************************************/ void test12() { static int opeq; // xopEquals OK static struct S1a { const bool opEquals( const typeof(this) rhs) { ++opeq; return false; } } static struct S1b { const bool opEquals(ref const typeof(this) rhs) { ++opeq; return false; } } static struct S1c { const bool opEquals( typeof(this) rhs) { ++opeq; return false; } } // xopEquals NG static struct S2a { bool opEquals( typeof(this) rhs) { ++opeq; return false; } } foreach (S; Seq!(S1a, S1b, S1c)) { S s; opeq = 0; assert(s != s); // call opEquals directly assert(!typeid(S).equals(&s, &s)); // -> xopEquals (-> __xopEquals) -> opEquals assert(opeq == 2); } foreach (S; Seq!(S2a)) { S s; opeq = 0; assert(s != s); assert(thrown!Error(!typeid(S).equals(&s, &s))); // Error("notImplemented") thrown assert(opeq == 1); } } /**************************************/ void test13() { static int opeq; struct X { const bool opEquals(const X){ ++opeq; return false; } } struct S { X x; } S makeS(){ return S(); } S s; opeq = 0; assert(s != s); assert(makeS() != s); assert(s != makeS()); assert(makeS() != makeS()); assert(opeq == 4); // built-in opEquals == const bool opEquals(const S rhs); assert(s != s); assert(opeq == 5); // xopEquals assert(!typeid(S).equals(&s, &s)); assert(opeq == 6); } /**************************************/ void test14() { static int opeq; struct S { const bool opEquals(T)(const T rhs) { ++opeq; return false; } } S makeS(){ return S(); } S s; opeq = 0; assert(s != s); assert(makeS() != s); assert(s != makeS()); assert(makeS() != makeS()); assert(opeq == 4); // xopEquals (-> __xxopEquals) -> template opEquals assert(!typeid(S).equals(&s, &s)); assert(opeq == 5); } /**************************************/ void test15() { struct S { const bool opEquals(T)(const(T) rhs) if (!is(T == S)) { return false; } @disable const bool opEquals(T)(const(T) rhs) if (is(T == S)) { return false; } } S makeS(){ return S(); } S s; static assert(!__traits(compiles, s != s)); static assert(!__traits(compiles, makeS() != s)); static assert(!__traits(compiles, s != makeS())); static assert(!__traits(compiles, makeS() != makeS())); // xopEquals (-> __xxopEquals) -> Error thrown assert(thrown!Error(!typeid(S).equals(&s, &s))); } /**************************************/ void test16() { struct X { int n; const bool opEquals(T)(T t) { return false; } } struct S { X x; } S s1, s2; assert(s1 != s2); // field template opEquals should call } /**************************************/ void test17() { static int opeq = 0; struct S { bool opEquals(ref S rhs) { ++opeq; return false; } } S[] sa1 = new S[3]; S[] sa2 = new S[3]; assert(sa1 != sa2); // isn't used TypeInfo.equals assert(opeq == 1); const(S)[] csa = new const(S)[3]; static assert(!__traits(compiles, csa == sa1)); static assert(!__traits(compiles, sa1 == csa)); static assert(!__traits(compiles, csa == csa)); } /**************************************/ // 3789 bool test3789() { static struct Float { double x; } Float f; assert(f.x != f.x); // NaN != NaN assert(f != f); static struct Array { int[] x; } Array a1 = Array([1,2,3].dup); Array a2 = Array([1,2,3].dup); if (!__ctfe) { // Currently doesn't work this in CTFE - may or may not a bug. assert(a1.x !is a2.x); } assert(a1.x == a2.x); assert(a1 == a2); static struct AA { int[int] x; } AA aa1 = AA([1:1,2:2,3:3]); AA aa2 = AA([1:1,2:2,3:3]); if (!__ctfe) { // Currently doesn't work this in CTFE - may or may not a bug. assert(aa1.x !is aa2.x); } if (!__ctfe) { // This is definitely a bug. Should work in CTFE. assert(aa1.x == aa2.x); assert(aa1 == aa2); } if (!__ctfe) { // Currently union operation is not supported in CTFE. union U1 { double x; } static struct UnionA { int[] a; U1 u; } auto ua1 = UnionA([1,2,3]); auto ua2 = UnionA([1,2,3]); assert(ua1.u.x is ua2.u.x); assert(ua1.u.x != ua2.u.x); assert(ua1 == ua2); ua1.u.x = 1.0; ua2.u.x = 1.0; assert(ua1.u.x is ua2.u.x); assert(ua1.u.x == ua2.u.x); assert(ua1 == ua2); ua1.u.x = double.nan; assert(ua1.u.x !is ua2.u.x); assert(ua1.u.x != ua2.u.x); assert(ua1 != ua2); union U2 { int[] a; } static struct UnionB { double x; U2 u; } auto ub1 = UnionB(1.0); auto ub2 = UnionB(1.0); assert(ub1 == ub2); ub1.u.a = [1,2,3].dup; ub2.u.a = [1,2,3].dup; assert(ub1.u.a !is ub2.u.a); assert(ub1.u.a == ub2.u.a); assert(ub1 != ub2); ub2.u.a = ub1.u.a; assert(ub1.u.a is ub2.u.a); assert(ub1.u.a == ub2.u.a); assert(ub1 == ub2); } if (!__ctfe) { // This is definitely a bug. Should work in CTFE. static struct Class { Object x; } static class X { override bool opEquals(Object o){ return true; } } Class c1a = Class(new Object()); Class c2a = Class(new Object()); assert(c1a.x !is c2a.x); assert(c1a.x != c2a.x); assert(c1a != c2a); // Pass, Object.opEquals works like bitwise compare Class c1b = Class(new X()); Class c2b = Class(new X()); assert(c1b.x !is c2b.x); assert(c1b.x == c2b.x); assert(c1b == c2b); // Fails, should pass } return true; } static assert(test3789()); /**************************************/ // 10037 struct S10037 { bool opEquals(ref const S10037) { assert(0); } } struct T10037 { S10037 s; // Compiler should not generate 'opEquals' here implicitly: } struct Sub10037(TL...) { TL data; int value; alias value this; } void test10037() { S10037 s; T10037 t; static assert( __traits(hasMember, S10037, "opEquals")); static assert(!__traits(hasMember, T10037, "opEquals")); assert(thrown!Error(s == s)); assert(thrown!Error(t == t)); Sub10037!(S10037) lhs; Sub10037!(S10037) rhs; static assert(!__traits(hasMember, Sub10037!(S10037), "opEquals")); assert(lhs == rhs); // lowered to: lhs.value == rhs.value } /**************************************/ // 5810 struct Bug5810 { void opUnary(string op)() {} } struct Foo5810 { Bug5810 x; void bar() { x++; } } /**************************************/ // 6798 struct Tuple6798(T...) { T field; alias field this; bool opEquals(Tuple6798 rhs) { foreach (i, _; T) { if (this[i] != rhs[i]) return false; } return true; } } auto tuple6798(T...)(T args) { return Tuple6798!T(args); } int test6798a() { //import std.typecons; alias tuple6798 tuple; static struct S1 { auto opDollar(size_t dim)() { return 99; } auto opSlice(int dim)(int lwr, int upr) { return [dim, lwr, upr]; } auto opIndex(A...)(A indices) { return tuple(" []", indices); } auto opIndexUnary(string op, A...)(A indices) { return tuple(op~"[]", indices); } auto opIndexAssign(A...)(string s, A indices) { return tuple("[] =", s, indices); } auto opIndexOpAssign(string op, A...)(string s, A indices) { return tuple("[]"~op~"=", s, indices); } } S1 s1; assert( s1[] == tuple(" []")); assert( s1[10] == tuple(" []", 10)); assert( s1[10, 20] == tuple(" []", 10, 20)); assert( s1[10..20] == tuple(" []", [0, 10, 20])); assert(+s1[] == tuple("+[]")); assert(-s1[10] == tuple("-[]", 10)); assert(*s1[10, 20] == tuple("*[]", 10, 20)); assert(~s1[10..20] == tuple("~[]", [0, 10, 20])); assert((s1[] ="x") == tuple("[] =", "x")); assert((s1[10] ="x") == tuple("[] =", "x", 10)); assert((s1[10, 20] ="x") == tuple("[] =", "x", 10, 20)); assert((s1[10..20] ="x") == tuple("[] =", "x", [0, 10, 20])); assert((s1[] +="x") == tuple("[]+=", "x")); assert((s1[10] -="x") == tuple("[]-=", "x", 10)); assert((s1[10, 20]*="x") == tuple("[]*=", "x", 10, 20)); assert((s1[10..20]~="x") == tuple("[]~=", "x", [0, 10, 20])); assert( s1[20..30, 10] == tuple(" []", [0, 20, 30], 10)); assert( s1[10, 10..$, $-4, $..2] == tuple(" []", 10, [1,10,99], 99-4, [3,99,2])); assert(+s1[20..30, 10] == tuple("+[]", [0, 20, 30], 10)); assert(-s1[10, 10..$, $-4, $..2] == tuple("-[]", 10, [1,10,99], 99-4, [3,99,2])); assert((s1[20..30, 10] ="x") == tuple("[] =", "x", [0, 20, 30], 10)); assert((s1[10, 10..$, $-4, $..2] ="x") == tuple("[] =", "x", 10, [1,10,99], 99-4, [3,99,2])); assert((s1[20..30, 10] +="x") == tuple("[]+=", "x", [0, 20, 30], 10)); assert((s1[10, 10..$, $-4, $..2]-="x") == tuple("[]-=", "x", 10, [1,10,99], 99-4, [3,99,2])); // opIndex exist, but opSlice for multi-dimensional doesn't. static struct S2 { auto opSlice(size_t dim)() { return [dim]; } auto opSlice()(size_t lwr, size_t upr) { return [lwr, upr]; } auto opIndex(A...)(A indices){ return [[indices]]; } } S2 s2; assert(s2[] == [[]]); assert(s2[1] == [[1]]); assert(s2[1, 2] == [[1, 2]]); assert(s2[1..2] == [1, 2]); static assert(!__traits(compiles, s2[1, 2..3] )); static assert(!__traits(compiles, s2[1..2, 2..3] )); // opSlice for multi-dimensional exists, but opIndex for that doesn't. static struct S3 { auto opSlice(size_t dim)(size_t lwr, size_t upr) { return [lwr, upr]; } auto opIndex(size_t n){ return [[n]]; } auto opIndex(size_t n, size_t m){ return [[n, m]]; } } S3 s3; static assert(!__traits(compiles, s3[] )); assert(s3[1] == [[1]]); assert(s3[1, 2] == [[1, 2]]); static assert(!__traits(compiles, s3[1..2] )); static assert(!__traits(compiles, s3[1, 2..3] )); static assert(!__traits(compiles, s3[1..2, 2..3] )); return 0; } int test6798b() { static struct Typedef(T) { private T Typedef_payload = T.init; alias a = Typedef_payload; auto ref opIndex(this X, D...)(auto ref D i) { return a[i]; } auto ref opSlice(this X )() { return a[]; } auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) { assert(b == 0 && e == 3); return a[b..e]; } template opDispatch(string name) { // field or property function @property auto ref opDispatch(this X)() { return mixin("a."~name); } @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); } } static if (is(typeof(a) : E[], E)) { auto opDollar() const { return a.length; } } } Typedef!(int[]) dollar2; dollar2.length = 3; assert(dollar2.Typedef_payload.length == 3); assert(dollar2[0 .. $] is dollar2[0 .. 3]); return 0; } int test6798c() { alias T = Tuple6798!(int, int); auto n = T[].init; static assert(is(typeof(n[0]) == Tuple6798!(int, int))); return 0; } void test6798() { static assert(test6798a() == 0); // CTFE check test6798a(); static assert(test6798b() == 0); test6798b(); static assert(test6798c() == 0); test6798c(); } /**************************************/ // 12382 struct S12382 { size_t opDollar() { return 0; } size_t opIndex(size_t) { return 0; } } S12382 func12382() { return S12382(); } static assert(S12382.init[$] == 0); static assert(func12382()[$] == 0); enum e12382a = S12382.init[$]; enum e12382b = func12382()[$]; static v12382a = S12382.init[$]; static v12382b = func12382()[$]; void test12382() { static assert(S12382.init[$] == 0); static assert(func12382()[$] == 0); enum e12382a = S12382.init[$]; enum e12382b = func12382()[$]; static v12382a = S12382.init[$]; static v12382b = func12382()[$]; } /**************************************/ // 12904 struct S12904 { void opIndexAssign(U, A...)(U value, A args) { static assert(0); } void opSliceAssign(int n) { assert(n == 10); } size_t opDollar(size_t dim)() { return 7; } int opSlice(size_t dim)(size_t, size_t to) { assert(to == 7); return 1; } int opIndex(int i1, int i2) { assert(i1 == 1 && i2 == 1); return 10; } } void test12904() { S12904 s; s[] = s[0..$, 1]; s[] = s[0..$, 0..$]; } /**************************************/ // 7641 mixin template Proxy7641(alias a) { auto ref opBinaryRight(string op, B)(auto ref B b) { return mixin("b "~op~" a"); } } struct Typedef7641(T) { private T Typedef_payload; this(T init) { Typedef_payload = init; } mixin Proxy7641!Typedef_payload; } void test7641() { class C {} C c1 = new C(); auto a = Typedef7641!C(c1); static assert(!__traits(compiles, { C c2 = a; })); } /**************************************/ // 8434 void test8434() { static class Vector2D(T) { T x, y; this(T x, T y) { this.x = x; this.y = y; } U opCast(U)() const { assert(0); } } alias Vector2D!(short) Vector2s; alias Vector2D!(float) Vector2f; Vector2s vs1 = new Vector2s(42, 23); Vector2s vs2 = new Vector2s(42, 23); assert(vs1 != vs2); } /**************************************/ void test18() { // one dimensional indexing static struct IndexExp { int[] opIndex(int a) { return [a]; } int[] opIndexUnary(string op)(int a) { return [a]; } int[] opIndexAssign(int val, int a) { return [val, a]; } int[] opIndexOpAssign(string op)(int val, int a) { return [val, a]; } int opDollar() { return 8; } } IndexExp index; // opIndex assert(index[8] == [8]); assert(index[$] == [8]); assert(index[$-1] == [7]); assert(index[$-$/2] == [4]); // opIndexUnary assert(-index[8] == [8]); assert(-index[$] == [8]); assert(-index[$-1] == [7]); assert(-index[$-$/2] == [4]); // opIndexAssign assert((index[8] = 2) == [2, 8]); assert((index[$] = 2) == [2, 8]); assert((index[$-1] = 2) == [2, 7]); assert((index[$-$/2] = 2) == [2, 4]); // opIndexOpAssign assert((index[8] += 2) == [2, 8]); assert((index[$] += 2) == [2, 8]); assert((index[$-1] += 2) == [2, 7]); assert((index[$-$/2] += 2) == [2, 4]); // opDollar is only one-dimensional static assert(!is(typeof(index[$, $]))); static assert(!is(typeof(-index[$, $]))); static assert(!is(typeof(index[$, $] = 2))); static assert(!is(typeof(index[$, $] += 2))); // multi dimensional indexing static struct ArrayExp { int[] opIndex(int a, int b) { return [a, b]; } int[] opIndexUnary(string op)(int a, int b) { return [a, b]; } int[] opIndexAssign(int val, int a, int b) { return [val, a, b]; } int[] opIndexOpAssign(string op)(int val, int a, int b) { return [val, a, b]; } int opDollar(int dim)() { return dim; } } ArrayExp array; // opIndex assert(array[8, 8] == [8, 8]); assert(array[$, $] == [0, 1]); assert(array[$, $-1] == [0, 0]); assert(array[2, $-$/2] == [2, 1]); // opIndexUnary assert(-array[8, 8] == [8, 8]); assert(-array[$, $] == [0, 1]); assert(-array[$, $-1] == [0, 0]); assert(-array[2, $-$/2] == [2, 1]); // opIndexAssign assert((array[8, 8] = 2) == [2, 8, 8]); assert((array[$, $] = 2) == [2, 0, 1]); assert((array[$, $-1] = 2) == [2, 0, 0]); assert((array[2, $-$/2] = 2) == [2, 2, 1]); // opIndexOpAssign assert((array[8, 8] += 2) == [2, 8, 8]); assert((array[$, $] += 2) == [2, 0, 1]); assert((array[$, $-1] += 2) == [2, 0, 0]); assert((array[2, $-$/2] += 2) == [2, 2, 1]); // one dimensional slicing static struct SliceExp { int[] opSlice(int a, int b) { return [a, b]; } int[] opSliceUnary(string op)(int a, int b) { return [a, b]; } int[] opSliceAssign(int val, int a, int b) { return [val, a, b]; } int[] opSliceOpAssign(string op)(int val, int a, int b) { return [val, a, b]; } int opDollar() { return 8; } } SliceExp slice; // opSlice assert(slice[0 .. 8] == [0, 8]); assert(slice[0 .. $] == [0, 8]); assert(slice[0 .. $-1] == [0, 7]); assert(slice[$-3 .. $-1] == [5, 7]); // opSliceUnary assert(-slice[0 .. 8] == [0, 8]); assert(-slice[0 .. $] == [0, 8]); assert(-slice[0 .. $-1] == [0, 7]); assert(-slice[$-3 .. $-1] == [5, 7]); // opSliceAssign assert((slice[0 .. 8] = 2) == [2, 0, 8]); assert((slice[0 .. $] = 2) == [2, 0, 8]); assert((slice[0 .. $-1] = 2) == [2, 0, 7]); assert((slice[$-3 .. $-1] = 2) == [2, 5, 7]); // opSliceOpAssign assert((slice[0 .. 8] += 2) == [2, 0, 8]); assert((slice[0 .. $] += 2) == [2, 0, 8]); assert((slice[0 .. $-1] += 2) == [2, 0, 7]); assert((slice[$-3 .. $-1] += 2) == [2, 5, 7]); // test different kinds of opDollar auto dollar(string opDollar)() { static struct Dollar { size_t opIndex(size_t a) { return a; } mixin(opDollar); } Dollar d; return d[$]; } assert(dollar!q{@property size_t opDollar() { return 8; }}() == 8); assert(dollar!q{template opDollar(size_t dim) { enum opDollar = dim; }}() == 0); assert(dollar!q{static const size_t opDollar = 8;}() == 8); assert(dollar!q{enum opDollar = 8;}() == 8); assert(dollar!q{size_t length() { return 8; } alias length opDollar;}() == 8); } /**************************************/ void test19() { static struct Foo { int[] opSlice(int a, int b) { return [a, b]; } int opDollar(int dim)() { return dim; } } Foo foo; assert(foo[0 .. $] == [0, 0]); } /**************************************/ // 9453 struct Foo9453 { static int ctor = 0; this(string bar) { ++ctor; } void opIndex(size_t i) const {} void opSlice(size_t s, size_t e) const {} size_t opDollar(int dim)() const if (dim == 0) { return 1; } } void test9453() { assert(Foo9453.ctor == 0); Foo9453("bar")[$-1]; assert(Foo9453.ctor == 1); Foo9453("bar")[0..$]; assert(Foo9453.ctor == 2); } /**************************************/ // 9496 struct S9496 { static S9496* ptr; size_t opDollar() { assert(ptr is &this); return 10; } void opSlice(size_t , size_t) { assert(ptr is &this); } void getSlice() { assert(ptr is &this); this[1 .. opDollar()]; this[1 .. $]; } } void test9496() { S9496 s; S9496.ptr = &s; s.getSlice(); s[1 .. $]; } /**************************************/ // 9689 struct B9689(T) { T val; @disable this(this); bool opEquals(this X, B)(auto ref B b) { //pragma(msg, "+", X, ", B = ", B, ", ref = ", __traits(isRef, b)); return this.val == b.val; //pragma(msg, "-", X, ", B = ", B, ", ref = ", __traits(isRef, b)); } } struct S9689 { B9689!int num; } void test9689() { B9689!S9689 b; } /**************************************/ // 9694 struct S9694 { bool opEquals(ref S9694 rhs) { assert(0); } } struct T9694 { S9694 s; } void test9694() { T9694 t; assert(thrown!Error(typeid(T9694).equals(&t, &t))); } /**************************************/ // 10064 void test10064() { static struct S { int x = 3; @disable this(); this(int) { x = 7; } int opSlice(size_t, size_t) { return 0; } @property size_t opDollar() { assert(x == 7 || x == 3); // fails assert(x == 7); return 0; } } auto x = S(0)[0 .. $]; } /**************************************/ // 12585 void test12585() { struct Bar { int opIndex(size_t index) { return 0; } } struct Foo { Bar opIndex(size_t index) { throw new Exception("Fail"); } } Foo foo() { return Foo(); } void catchStuff(E)(lazy E expression) { try expression(); catch (Exception e) {} } catchStuff(foo()[0][0]); // OK <- NG catchStuff(foo().opIndex(0)[0]); // OK catchStuff(foo()[0].opIndex(0)); // OK Foo f; catchStuff(f[0][0]); // OK } /**************************************/ // 10394 void test10394() { alias Seq!(int, int) Pair; Pair pair; struct S1 { int opBinary(string op)(Pair) { return 1; } bool opEquals(Pair) { return true; } int opOpAssign(string op)(Pair) { return 1; } } S1 s1; assert((s1 + pair) == 1); assert((s1 == pair) == true); assert((s1 *= pair) == 1); struct S2 { int opBinaryRight(string op)(Pair lhs) { return 1; } int opCmp(Pair) { return -1; } } S2 s2; assert((pair in s2) == 1); assert(s2 < pair); } /**************************************/ // 10597 struct R10597 { void opIndex(int) {} void opSlice(int, int) {} int opDollar(); } R10597 r; struct S10597 { static assert(is(typeof(r[0]))); //ok static assert(is(typeof(r[$]))); //fails static assert(is(typeof(r[0..0]))); //ok static assert(is(typeof(r[$..$]))); //fails void foo() { static assert(is(typeof(r[0]))); //ok static assert(is(typeof(r[$]))); //ok static assert(is(typeof(r[0..0]))); //ok static assert(is(typeof(r[$..$]))); //ok } } static assert(is(typeof(r[0]))); //ok static assert(is(typeof(r[$]))); //fails static assert(is(typeof(r[0..0]))); //ok static assert(is(typeof(r[$..$]))); //fails void test10597() { static assert(is(typeof(r[0]))); //ok static assert(is(typeof(r[$]))); //ok static assert(is(typeof(r[0..0]))); //ok static assert(is(typeof(r[$..$]))); //ok } /**************************************/ // 10567 // doesn't require thunk struct S10567x1n { int value; int opCmp(ref const S10567x1n rhs) const { return 0; } } // requires thunk struct S10567y1n { int value; int opCmp(const S10567y1n rhs) const { return 0; } } struct S10567y1t { int value; int opCmp(S)(const S rhs) const { return 0; } } // doesn't support const comparison struct S10567z1n { int value; int opCmp(const S10567z1n rhs) { return 0; } } struct S10567z1t { int value; int opCmp(S)(const S rhs) { return 0; } } /+ struct S10567x2n { S10567x1n s; this(int n) { s = typeof(s)(n); } alias s this; } struct S10567y2n { S10567y1n s; this(int n) { s = typeof(s)(n); } alias s this; } struct S10567y2t { S10567y1t s; this(int n) { s = typeof(s)(n); } alias s this; } struct S10567z2n { S10567z1n s; this(int n) { s = typeof(s)(n); } alias s this; } struct S10567z2t { S10567z1t s; this(int n) { s = typeof(s)(n); } alias s this; } struct S10567d1 { int value; int opDispatch(string name, S)(const S rhs) const if (name == "opCmp") { assert(0); } } struct S10567d2 { int value; template opDispatch(string name) if (name == "opCmp") { int opDispatch(const S rhs) const { assert(0); } } } // recursive alias this + opCmp searching struct S10567r1 { static S10567r2 t; ref S10567r2 payload() { return t; } alias payload this; int opCmp(const S10567r1 s) const { return 0; } } struct S10567r2 { static S10567r1 s; ref S10567r1 payload() { return s; } alias payload this; } +/ void test10567() { foreach (S; Seq!(S10567x1n/+, S10567x2n+/)) { S sx = S(1); S sy = S(2); assert(!(sx < sy) && !(sx > sy)); assert(sx.opCmp(sy) == 0); assert(typeid(S).compare(&sx, &sy) == 0); static if (is(S == S10567x1n)) assert(cast(void*)typeid(S).xopCmp == cast(void*)&S.opCmp, S.stringof); } foreach (S; Seq!(S10567y1n, S10567y1t/+, S10567y2n, S10567y2t+/)) { S sx = S(1); S sy = S(2); assert(!(sx < sy) && !(sx > sy)); assert(sx.opCmp(sy) == 0); assert(typeid(S).compare(&sx, &sy) == 0); } foreach (S; Seq!(S10567z1n, S10567z1t/+, S10567z2n, S10567z2t+/)) { S sx = S(1); S sy = S(2); assert(!(sx < sy) && !(sx > sy)); assert(sx.opCmp(sy) == 0); try { auto x = typeid(S).compare(&sx, &sy); assert(0); } catch (Error e) { assert(e.msg[$-15 .. $] == "not implemented"); } } /+ foreach (S; Seq!(S10567d1, S10567d2)) { int[S] aa; aa[S(1)] = 10; aa[S(1)] = 1; aa[S(2)] = 20; aa[S(2)] = 2; assert(aa.length == 2); foreach (k, v; aa) assert(k.value == v); S sx = S(1); S sy = S(2); // Don't invoke opDispatch!"opCmp" assert(typeid(S).compare(&sx, &sy) != 0); } +/ } /**************************************/ // 11062 struct S11062ia { struct S1 { void opIndexAssign(int val, int key) {} } struct S2 { S1 headers; } private S2 m_obj; @property S2 get() { return m_obj; } alias get this; } struct S11062sa { struct S1 { void opSliceAssign(int val, int lwr, int upr) {} } struct S2 { S1 headers; } private S2 m_obj; @property S2 get() { return m_obj; } alias get this; } void test11062() { auto sia = S11062ia(); sia.headers[1] = 1; // bug auto ssa = S11062sa(); ssa.headers[1..2] = 1; // bug } /**************************************/ // 11311 void test11311() { static int ctor, cpctor, dtor; static struct S { this(int) { ++ctor; } this(this) { ++cpctor; } ~this() { ++dtor; } } static struct Arr { S data; ref S opIndex(int) return { return data; } ref S opSlice(int, int) return { return data; } } { Arr a = Arr(S(1)); assert(ctor == 1); assert(cpctor == 0); assert(dtor == 0); auto getA1() { return a; } //getA1().opIndex(1); // OK getA1()[1]; // NG assert(ctor == 1); assert(cpctor == 1); // getA() returns a copy of a assert(dtor == 1); // temporary returned by getA() should be destroyed } assert(dtor == 2); assert(ctor + cpctor == dtor); ctor = cpctor = dtor = 0; { Arr a = Arr(S(1)); assert(ctor == 1); assert(cpctor == 0); assert(dtor == 0); auto getA2() { return a; } //getA2().opSlice(1, 2); // OK getA2()[1..2]; // NG assert(ctor == 1); assert(cpctor == 1); // getA() returns a copy of a assert(dtor == 1); // temporary returned by getA() should be destroyed } assert(dtor == 2); assert(ctor + cpctor == dtor); } /**************************************/ // 12193 void test12193() { struct Foo { bool bar; alias bar this; void opOpAssign(string op)(size_t x) { bar = false; } } Foo foo; foo <<= 1; } /**************************************/ // 14057 struct W14057 { int[] subType; alias subType this; W14057 opSlice(size_t, size_t) { return this; } } void test14057() { auto w = W14057(); W14057 w2 = w[0 .. 1337]; } /**************************************/ struct Tuple20(T...) { T field; alias field this; } void test20a() { // ae1save in in AssignExp::semantic int a, b; struct A1 { void opIndexAssign(int v, Tuple20!(int, int) ) { a = v; } Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; } } struct A2 { A1 a1; alias a1 this; int opIndexAssign(int) { return b; } } stompStack(); A2 foo() { return A2(); } foo()[1..2] = 1; // ref A1 __tmp = foo().a1; __tmp.opIndexAssign(1, __tmp.opSlice!0(1, 2)); assert(a == 1); // should work assert(b == 0); } void test20b() { // ae1save in UnaExp::op_overload() int a, b; struct A1 { void opIndexUnary(string op)(Tuple20!(int, int) ) { a = 1; } Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; } void dummy() {} // nessary to make A1 nested struct } struct A2 { A1 a1; alias a1 this; int opIndexUnary(string op)(int) { return 0; } } stompStack(); A2 foo() { return A2(); } +foo()[1..2]; // ref A1 __tmp = foo().a1; __tmp.opIndexUnary!"+"(__tmp.opSlice!0(1, 2)); assert(a == 1); // should pass assert(b == 0); } void test20c() { // ae1save in ArrayExp::op_overload() int a, b; struct A1 { void opIndex(Tuple20!(int, int) ) { a = 1; } Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; } } struct A2 { A1 a1; alias a1 this; int opIndex(int) { return 0; } } stompStack(); A2 foo() { return A2(); } foo()[1..2]; // ref A1 __tmp = foo().a1; __tmp.opIndex(__tmp.opSlice!0(1, 2)); assert(a == 1); // should pass assert(b == 0); } void test20d() { // ae1save in BinAssignExp::op_overload() int a, b; struct A1 { void opIndexOpAssign(string op)(int v, Tuple20!(int, int) ) { a = v; } Tuple20!(int, int) opSlice(size_t dim)(int, int) { return typeof(return).init; } void dummy() {} // nessary to make A1 nested struct } struct A2 { A1 a1; alias a1 this; ref int opIndexOpAssign(alias op)(int) { return b; } } stompStack(); A2 foo() { return A2(); } foo()[1..2] += 1; // ref A1 __tmp = foo().a1; __tmp.opIndexOpAssign!"+"(1, __tmp.opSlice!0(1, 2)); assert(a == 1); // should pass assert(b == 0); } /**************************************/ // 14624 void test14624() { struct A1 { int x; ref int opIndex() return { return x; } ref int opSlice() { assert(0); } } { A1 a = A1(1); auto x = a[]; // a.opIndex() assert(x == a.x); auto y = -a[]; // -a.opIndex() <-- not found: a.opIndexUnary!"-" assert(y == -a.x); a[] = 1; // a.opIndex() = 1; <-- not found: a.opIndexAssign(int) assert(a.x == 1); a[] += 1; // a.opIndex() += 1; <-- not found: a.opIndexOpAssign!"+"(int) assert(a.x == 2); } struct A2 { int x; ref int opIndex() return { x = 10; return x; } ref int opSlice() { assert(0); } ref int opSliceUnary(alias op)() { x = 11; return x; } ref int opSliceAssign(int) return { x = 12; return x; } ref int opSliceOpAssign(alias op)(int) { x = 13; return x; } } { A2 a = A2(1); auto x = a[]; // a.opIndex() assert(a.x == 10); auto y = -a[]; // a.opSliceUnary!"-"() is preferred than: -a.opIndex() assert(a.x == 11); a[] = 1; // a.opSliceAssign(1) is preferred than: a.opIndex() = 1; assert(a.x == 12); a[] += 1; // a.opSliceOpAssign!"+"(1) is preferred than: a.opIndex() += 1; assert(a.x == 13); } } /**************************************/ // 14625 void test14625() { struct R { @property bool empty() { return true; } @property int front() { return 0; } void popFront() {} } struct C1 { R opIndex() { return R(); } R opSlice() { assert(0); } } C1 c1; foreach (e; c1) {} // OK <- asserts in opSlice() foreach (e; c1[]) {} // OK, opIndex() struct C2 { R opIndex() { return R(); } } C2 c2; foreach (e; c2) {} // OK <- rejected foreach (e; c2[]) {} // OK, opIndex() } /**************************************/ int main() { test1(); test2(); test3(); test4(); test5(); test6(); test7(); test8(); test9(); test10(); test11(); test4099(); test12(); test13(); test14(); test15(); test16(); test17(); test3789(); test10037(); test6798(); test12904(); test7641(); test8434(); test18(); test19(); test9453(); test9496(); test9689(); test9694(); test10064(); test12585(); test10394(); test10567(); test11062(); test11311(); test14057(); test20a(); test20b(); test20c(); test20d(); test14624(); test14625(); printf("Success\n"); return 0; }