| /* |
| RUN_OUTPUT: |
| --- |
| Success |
| --- |
| */ |
| // REQUIRED_ARGS: |
| |
| // Test array bounds checking |
| |
| import core.exception; |
| extern(C) int printf(const char*, ...); |
| |
| template TypeTuple(T...) { alias T TypeTuple; } |
| |
| /******************************************/ |
| |
| const int[10] foos = [1,2,3,4,5,6,7,8,9,10]; |
| const int[] food = [21,22,23,24,25,26,27,28,29,30]; |
| const int *foop = cast(int*) foos; |
| |
| static int x = 2; |
| |
| int index() |
| { |
| return x++; |
| } |
| |
| int tests(int i) |
| { |
| return foos[index()]; |
| } |
| |
| int testd(int i) |
| { |
| return food[index()]; |
| } |
| |
| int testp(int i) |
| { |
| return foop[i]; |
| } |
| |
| const(int)[] slices(int lwr, int upr) |
| { |
| return foos[lwr .. upr]; |
| } |
| |
| const(int)[] sliced(int lwr, int upr) |
| { |
| return food[lwr .. upr]; |
| } |
| |
| const(int)[] slicep(int lwr, int upr) |
| { |
| return foop[lwr .. upr]; |
| } |
| |
| void test1() |
| { |
| int i; |
| |
| i = tests(0); |
| assert(i == 3); |
| |
| i = testd(0); |
| assert(i == 24); |
| |
| i = testp(1); |
| assert(i == 2); |
| |
| x = 10; |
| try |
| { |
| i = tests(0); |
| } |
| catch (RangeError a) |
| { |
| i = 73; |
| } |
| assert(i == 73); |
| |
| x = -1; |
| try |
| { |
| i = testd(0); |
| } |
| catch (RangeError a) |
| { |
| i = 37; |
| } |
| assert(i == 37); |
| |
| const(int)[] r; |
| |
| r = slices(3,5); |
| assert(r[0] == foos[3]); |
| assert(r[1] == foos[4]); |
| |
| r = sliced(3,5); |
| assert(r[0] == food[3]); |
| assert(r[1] == food[4]); |
| |
| r = slicep(3,5); |
| assert(r[0] == foos[3]); |
| assert(r[1] == foos[4]); |
| |
| try |
| { |
| i = 7; |
| r = slices(5,3); |
| } |
| catch (RangeError a) |
| { |
| i = 53; |
| } |
| assert(i == 53); |
| |
| try |
| { |
| i = 7; |
| r = slices(5,11); |
| } |
| catch (RangeError a) |
| { |
| i = 53; |
| } |
| assert(i == 53); |
| |
| try |
| { |
| i = 7; |
| r = sliced(5,11); |
| } |
| catch (RangeError a) |
| { |
| i = 53; |
| } |
| assert(i == 53); |
| |
| try |
| { |
| i = 7; |
| r = slicep(5,3); |
| } |
| catch (RangeError a) |
| { |
| i = 53; |
| } |
| assert(i == 53); |
| |
| // Take side effects into account |
| x = 1; |
| r = foos[index() .. 3]; |
| assert(x == 2); |
| assert(r[0] == foos[1]); |
| assert(r[1] == foos[2]); |
| |
| r = foos[1 .. index()]; |
| assert(r.length == 1); |
| assert(x == 3); |
| assert(r[0] == foos[1]); |
| |
| x = 1; |
| r = food[index() .. 3]; |
| assert(x == 2); |
| assert(r[0] == food[1]); |
| assert(r[1] == food[2]); |
| |
| r = food[1 .. index()]; |
| assert(r.length == 1); |
| assert(x == 3); |
| assert(r[0] == food[1]); |
| |
| x = 1; |
| r = foop[index() .. 3]; |
| assert(x == 2); |
| assert(r[0] == foop[1]); |
| assert(r[1] == foop[2]); |
| |
| r = foop[1 .. index()]; |
| assert(r.length == 1); |
| assert(x == 3); |
| assert(r[0] == foop[1]); |
| } |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=13976 |
| |
| void test13976() |
| { |
| int[] da = new int[](10); |
| int[10] sa; |
| enum size_t two = 2; |
| enum size_t five = 5; |
| size_t lb = 0; // upperInRange |
| size_t ub = 9; // | lowerLessThan |
| // | | check code |
| { auto s = da[lb .. ub]; } // 0 0 (ub <= $ && lb <= ub ) |
| { auto s = da[1 .. ub]; } // 0 0 (ub <= $ && 1 <= ub ) |
| { auto s = da[lb .. 10]; } // 0 0 (10 <= $ && lb <= 10 ) |
| { auto s = da[1 .. ub%5]; } // 0 0 (ub%5 <= $ && 1 <= ub%5) |
| |
| { auto s = da[lb .. ub]; } // 0 0 (ub <= $ && lb <= ub ) |
| { auto s = da[0 .. ub]; } // 0 1 (ub <= $ ) |
| { auto s = da[lb .. 10]; } // 0 0 (10 <= $ && lb <= 10 ) |
| { auto s = da[0 .. ub%5]; } // 0 1 (ub%5 <= $ ) |
| |
| { auto s = da[0 .. 0]; } // 1 1 NULL |
| { auto s = da[0 .. $]; } // 1 1 NULL |
| { auto s = da[1 .. $]; } // 1 0 ( 1 <= $ ) |
| { auto s = da[$ .. $]; } // 1 0 ( $ <= $ ) |
| { auto s = da[0 .. $/two]; } // 0 1 ($/2 <= $ ) |
| { auto s = da[$/two .. $]; } // 1 0 ( $/2 <= $ ) |
| { auto s = da[$/five .. $/two]; } // 0 0 ($/2 <= $ && $/5 <= $/2) |
| |
| { auto s = sa[lb .. ub]; } // 0 0 (ub <= 10 && lb <= ub ) |
| { auto s = sa[1 .. ub]; } // 0 0 (ub <= 10 && 1 <= ub ) |
| { auto s = sa[lb .. 10]; } // 1 0 ( lb <= 10 ) |
| { auto s = sa[1 .. ub%5]; } // 1 0 ( 1 <= ub%5) |
| |
| { auto s = sa[lb .. ub]; } // 0 0 (ub <= 10 && lb <= ub ) |
| { auto s = sa[0 .. ub]; } // 0 1 (ub <= 10 ) |
| { auto s = sa[lb .. 10]; } // 1 0 ( lb <= 10 ) |
| { auto s = sa[0 .. ub%5]; } // 1 1 NULL |
| |
| { auto s = sa[0 .. 0]; } // 1 1 NULL |
| { auto s = sa[0 .. $]; } // 1 1 NULL |
| { auto s = sa[1 .. $]; } // 1 1 NULL |
| { auto s = sa[$ .. $]; } // 1 1 NULL |
| { auto s = sa[0 .. $/two]; } // 1 1 NULL |
| { auto s = sa[$/two .. $]; } // 1 1 NULL |
| { auto s = sa[$/five .. $/two]; } // 1 1 NULL |
| |
| int* p = new int[](10).ptr; |
| { auto s = p[0 .. ub]; } // 1 1 NULL |
| { auto s = p[lb .. ub]; } // 1 0 (lb <= ub ) |
| { auto s = p[0 .. ub%5]; } // 1 1 NULL |
| { auto s = p[1 .. ub%5]; } // 1 0 (1 <= ub%5) |
| { auto s = p[0 .. 0]; } // 1 1 NULL |
| } |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=3652 |
| |
| void test3652() |
| { |
| int foo(int[4] x) |
| { |
| return x[0] + x[1] * x[2] - x[3]; |
| } |
| |
| int[] xs = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; |
| |
| // simple case |
| foo(xs[0 .. 4]); |
| |
| version(none) |
| { |
| // Need deformation of formula and detection of base point |
| int x = 0; |
| int y = 0; |
| foreach (i; 0 .. 4) |
| { |
| x += foo(xs[i .. i + 4]); |
| y += foo(xs[(i*4+10)/2 .. (i*8>>1)/2+9]); |
| // lwr = (i*4 + 10)/2 = i*4/2 + 10/2 = (i*2+5) |
| // upr = (i*8>>1)/2 + 5 = (i*4/2) + 5 = i*2 + 9 = (i*2+5) + 4 |
| } |
| assert(x == (0,1,2,3) + (1,2,3, 4) + (2, 3, 4, 5) + ( 3, 4, 5, 6)); |
| assert(y == (5,6,7,8) + (7,8,9,10) + (9,10,11,12) + (11,12,13,14)); |
| } |
| } |
| |
| void test3652a() @safe |
| { |
| string str = "aaaabbbbccccdddd"; |
| //printf("str.ptr = %p\n", str.ptr); |
| |
| void foo(ref const(char)[16] buf) |
| { |
| //printf("buf.ptr = %p\n", buf.ptr); |
| assert(buf.ptr is str.ptr); |
| } |
| |
| // can check length at runtime |
| assert(str.length == 16); |
| |
| // compiler can check the length of string literal, so |
| // conversion from immutable(char)[] to ref const(char)[16] is allowed; |
| static assert(__traits(compiles, foo("aaaabbbbccccdddd"))); |
| |
| // OK, correctly rejected by the compiler. |
| static assert(!__traits(compiles, foo(str[]))); |
| |
| // Ugly, furthermore does not work in safe code! |
| //foo(*cast(const(char)[16]*)(str[0..16].ptr)); |
| |
| // New: compiler can check the length of slice, but currently it is not allowed. |
| enum size_t m = 0; |
| size_t calc(){ return 0; } |
| foo(str[0 .. 16]); |
| foo(str[m .. 16]); |
| //foo(str[calc() .. 16]); // with CTFE |
| |
| // If boundaries cannot be calculated in compile time, it's rejected. |
| size_t n; |
| size_t calc2(){ return n; } |
| static assert(!__traits(compiles, foo(str[n .. 16]))); |
| static assert(!__traits(compiles, foo(str[calc2() .. 16]))); |
| |
| void hoo1(size_t dim)(char[dim]) { static assert(dim == 2); } |
| void hoo2(char[2]) {} |
| void hoo3(size_t dim)(ref char[dim]) {} |
| void hoo4(ref char[2]) {} |
| hoo1(str[0 .. 2]); |
| hoo2(str[0 .. 2]); |
| static assert(!__traits(compiles, hoo3(str[0 .. 2]))); |
| static assert(!__traits(compiles, hoo4(str[0 .. 2]))); |
| } |
| void test3652b() @safe |
| { |
| int[] da = [1,2,3,4,5]; |
| |
| void bar(int[3] sa1, ref int[3] sa2) |
| { |
| assert(sa1 == [1,2,3] && sa1.ptr !is da.ptr); |
| assert(sa2 == [1,2,3] && sa2.ptr is da.ptr); |
| } |
| bar(da[0..3], da[0..3]); |
| static assert(!__traits(compiles, bar(da[0..4], da[0..4]))); |
| |
| void baz1(T)(T[3] sa1, ref T[3] sa2) |
| { |
| assert(sa1 == [1,2,3] && sa1.ptr !is da.ptr); |
| assert(sa2 == [1,2,3] && sa2.ptr is da.ptr); |
| } |
| void baz2(T, size_t dim)(T[dim] sa1, ref T[dim] sa2, size_t result) |
| { |
| assert(dim == result); |
| static if (dim == 3) |
| { |
| assert(sa1 == [1,2,3] && sa1.ptr !is da.ptr); |
| assert(sa2 == [1,2,3] && sa2.ptr is da.ptr); |
| } |
| else |
| { |
| assert(sa1 == [1,2,3,4] && sa1.ptr !is da.ptr); |
| assert(sa2 == [1,2,3,4] && sa2.ptr is da.ptr); |
| } |
| } |
| baz1(da[0..3], da[0..3]); |
| static assert(!__traits(compiles, baz1(da[0..4], da[0..4]))); |
| baz2(da[0..3], da[0..3], 3); |
| baz2(da[0..4], da[0..4], 4); |
| |
| void hoo1(size_t dim)(int[dim]) { static assert(dim == 2); } |
| void hoo2(int[2]) {} |
| void hoo3(size_t dim)(ref int[dim]) {} |
| void hoo4(ref int[2]) {} |
| hoo1(da.idup[0 .. 2]); |
| hoo2(da.idup[0 .. 2]); |
| static assert(!__traits(compiles, hoo3(da.idup[0 .. 2]))); |
| static assert(!__traits(compiles, hoo4(da.idup[0 .. 2]))); |
| } |
| |
| /**********************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=9654 |
| |
| auto foo9654a(ref char[8] str) { return str; } |
| auto foo9654b(ref const char[8] str) { return str; } |
| auto foo9654c(ref immutable char[8] str) { return str; } |
| static assert(!is(typeof(foo9654a("testinfo")))); |
| static assert( is(typeof(foo9654b("testinfo")) == const char[8])); |
| static assert( is(typeof(foo9654c("testinfo")) == immutable char[8])); |
| |
| auto bar9654a(T)(ref T[8] str) { return str; static assert(is(T == immutable char)); } |
| auto bar9654b(T)(ref const T[8] str) { return str; static assert(is(T == char)); } |
| auto bar9654c(T)(ref immutable T[8] str) { return str; static assert(is(T == char)); } |
| static assert( is(typeof(bar9654a("testinfo")) == immutable char[8])); |
| static assert( is(typeof(bar9654b("testinfo")) == const char[8])); |
| static assert( is(typeof(bar9654c("testinfo")) == immutable char[8])); |
| |
| auto baz9654a(T, size_t dim)(ref T[dim] str) { return str; static assert(is(T == immutable char)); } |
| auto baz9654b(T, size_t dim)(ref const T[dim] str) { return str; static assert(is(T == char)); } |
| auto baz9654c(T, size_t dim)(ref immutable T[dim] str) { return str; static assert(is(T == char)); } |
| static assert( is(typeof(baz9654a("testinfo")) == immutable char[8])); |
| static assert( is(typeof(baz9654b("testinfo")) == const char[8])); |
| static assert( is(typeof(baz9654c("testinfo")) == immutable char[8])); |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=9712 |
| |
| auto func9712(T)(T[2] arg) { return arg; } |
| static assert(is(typeof(func9712([1,2])) == int[2])); |
| |
| auto deduceLength9712(T,size_t n)(T[n] a) { return a; } |
| static assert(is(typeof(deduceLength9712([1,2,3])) == int[3])); |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=9743 |
| |
| void test9743() |
| { |
| // +-Char |
| // |+-Immutable or Const or Mutable |
| // ||+-Value or Ref |
| // |||+-Function or +-Template |
| void fCIVF( immutable char[4]) {} void fCIVT()( immutable char[4]) {} |
| void fCCVF( const char[4]) {} void fCCVT()( const char[4]) {} |
| void fCMVF( char[4]) {} void fCMVT()( char[4]) {} |
| void fCIRF(ref immutable char[4]) {} void fCIRT()(ref immutable char[4]) {} |
| void fCCRF(ref const char[4]) {} void fCCRT()(ref const char[4]) {} |
| void fCMRF(ref char[4]) {} void fCMRT()(ref char[4]) {} |
| alias fcOK = TypeTuple!(fCIVF, fCIVT, fCCVF, fCCVT, fCMVF, fCMVT, fCIRF, fCIRT, fCCRF, fCCRT); |
| foreach (f; fcOK) f("1234" ) ; |
| foreach (f; fcOK) f("1234"c) ; |
| foreach (f; fcOK) static assert(!__traits(compiles, f("1234"w) )); |
| foreach (f; fcOK) static assert(!__traits(compiles, f("1234"d) )); |
| alias fcNG = TypeTuple!(fCMRF, fCMRT); // cannot hold immutable data by mutable ref |
| foreach (f; fcNG) static assert(!__traits(compiles, f("1234" ) )); |
| foreach (f; fcNG) static assert(!__traits(compiles, f("1234"c) )); |
| foreach (f; fcNG) static assert(!__traits(compiles, f("1234"w) )); |
| foreach (f; fcNG) static assert(!__traits(compiles, f("1234"d) )); |
| |
| // +-Wchar |
| void fWIVF( immutable wchar[4]) {} void fWIVT()( immutable wchar[4]) {} |
| void fWCVF( const wchar[4]) {} void fWCVT()( const wchar[4]) {} |
| void fWMVF( wchar[4]) {} void fWMVT()( wchar[4]) {} |
| void fWIRF(ref immutable wchar[4]) {} void fWIRT()(ref immutable wchar[4]) {} |
| void fWCRF(ref const wchar[4]) {} void fWCRT()(ref const wchar[4]) {} |
| void fWMRF(ref wchar[4]) {} void fWMRT()(ref wchar[4]) {} |
| alias fwOK = TypeTuple!(fWIVF, fWIVT, fWCVF, fWCVT, fWMVF, fWMVT, fWIRF, fWIRT, fWCRF, fWCRT); |
| foreach (f; fwOK) f("1234" ) ; |
| foreach (f; fwOK) static assert(!__traits(compiles, f("1234"c) )); |
| foreach (f; fwOK) f("1234"w) ; |
| foreach (f; fwOK) static assert(!__traits(compiles, f("1234"d) )); |
| alias fwNG = TypeTuple!(fWMRF, fWMRT); // cannot hold immutable data by mutable ref |
| foreach (f; fwNG) static assert(!__traits(compiles, f("1234" ) )); |
| foreach (f; fwNG) static assert(!__traits(compiles, f("1234"c) )); |
| foreach (f; fwNG) static assert(!__traits(compiles, f("1234"w) )); |
| foreach (f; fwNG) static assert(!__traits(compiles, f("1234"d) )); |
| |
| // +-Dchar |
| void fDIVF( immutable dchar[4]) {} void fDIVT()( immutable dchar[4]) {} |
| void fDCVF( const dchar[4]) {} void fDCVT()( const dchar[4]) {} |
| void fDMVF( dchar[4]) {} void fDMVT()( dchar[4]) {} |
| void fDIRF(ref immutable dchar[4]) {} void fDIRT()(ref immutable dchar[4]) {} |
| void fDCRF(ref const dchar[4]) {} void fDCRT()(ref const dchar[4]) {} |
| void fDMRF(ref dchar[4]) {} void fDMRT()(ref dchar[4]) {} |
| alias fdOK = TypeTuple!(fDIVF, fDIVT, fDCVF, fDCVT, fDMVF, fDMVT, fDIRF, fDIRT, fDCRF, fDCRT); |
| foreach (f; fdOK) f("1234" ) ; |
| foreach (f; fdOK) static assert(!__traits(compiles, f("1234"c) )); |
| foreach (f; fdOK) static assert(!__traits(compiles, f("1234"w) )); |
| foreach (f; fdOK) f("1234"d) ; |
| alias fdNG = TypeTuple!(fDMRF, fDMRT); // cannot hold immutable data by mutable ref |
| foreach (f; fdNG) static assert(!__traits(compiles, f("1234" ) )); |
| foreach (f; fdNG) static assert(!__traits(compiles, f("1234"c) )); |
| foreach (f; fdNG) static assert(!__traits(compiles, f("1234"w) )); |
| foreach (f; fdNG) static assert(!__traits(compiles, f("1234"d) )); |
| } |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=9747 |
| |
| void foo9747A(T)(T[4]) {} |
| void foo9747C(size_t dim)(char[dim]) {} |
| void foo9747W(size_t dim)(wchar[dim]) {} |
| void foo9747D(size_t dim)(dchar[dim]) {} |
| |
| void test9747() |
| { |
| foo9747A("abcd"c); |
| foo9747A("abcd"w); |
| foo9747A("abcd"d); |
| foo9747C("abcd"c); |
| foo9747W("abcd"w); |
| foo9747D("abcd"d); |
| } |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=12876 |
| |
| void test12876() |
| { |
| void foo(int[4] b) {} |
| void bar(size_t n)(int[n] c) { static assert(n == 4); } |
| |
| int[5] a; |
| foo(a[1 .. $]); // OK |
| bar(a[1 .. $]); // OK <- Error |
| } |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=13775 |
| |
| void test13775() |
| { |
| ubyte[4] ubytes = [1,2,3,4]; |
| |
| // CT-known slicing (https://issues.dlang.org/show_bug.cgi?id=3652) |
| auto ok1 = cast(ubyte[2]) ubytes[0 .. 2]; |
| assert(ok1 == [1, 2]); |
| |
| // CT-known slicing with implicit conversion of SliceExp::e1 (https://issues.dlang.org/show_bug.cgi?id=13154) |
| enum double[] arr = [1.0, 2.0, 3.0]; |
| auto ok2 = cast(float[2]) [1.0, 2.0, 3.0][0..2]; |
| auto ok3 = cast(float[2]) arr[1..3]; // currently this is accepted |
| assert(ok2 == [1f, 2f]); |
| assert(ok3 == [2f, 3f]); |
| |
| // CT-known slicing with type coercing (https://issues.dlang.org/show_bug.cgi?id=13775) |
| auto ok4 = cast( byte[2]) ubytes[0 .. 2]; // CT-known slicing + type coercing |
| auto ok5 = cast(short[1]) ubytes[0 .. 2]; // CT-known slicing + type coercing |
| assert(ok4 == [1, 2]); |
| version(LittleEndian) assert(ok5 == [0x0201]); |
| version( BigEndian) assert(ok5 == [0x0102]); |
| } |
| |
| /******************************************/ |
| // https://issues.dlang.org/show_bug.cgi?id=15889 |
| // Array bounds check should report index and length |
| void test15889() |
| { |
| int[] a = new int[2]; |
| |
| try { |
| a[3] = 40; |
| } catch (ArrayIndexError e) { |
| assert(e.index == 3); |
| assert(e.length == 2); |
| } |
| |
| try { |
| a[1 .. 4] = 50; |
| } catch (ArraySliceError e) { |
| assert(e.lower == 1); |
| assert(e.upper == 4); |
| assert(e.length == 2); |
| } |
| } |
| |
| /******************************************/ |
| |
| int main() |
| { |
| test1(); |
| test13976(); |
| test3652(); |
| test3652a(); |
| test3652b(); |
| test9743(); |
| test9747(); |
| test13775(); |
| test15889(); |
| |
| printf("Success\n"); |
| return 0; |
| } |