summaryrefslogtreecommitdiffstats
path: root/libphobos
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2022-02-28 15:47:52 +0100
committerIain Buclaw <ibuclaw@gdcproject.org>2022-02-28 17:49:01 +0100
commit1027dc459204894f4503f713a3d73826e4bbab15 (patch)
tree606564b15e0978c77c76d0e618c00c25a78aaf38 /libphobos
parentc++: Lost deprecated/unavailable attr in class tmpl [PR104682] (diff)
downloadgcc-1027dc459204894f4503f713a3d73826e4bbab15.tar.gz
gcc-1027dc459204894f4503f713a3d73826e4bbab15.tar.bz2
gcc-1027dc459204894f4503f713a3d73826e4bbab15.tar.xz
d: Merge upstream dmd cf63dd8e5, druntime caf14b0f, phobos 41aaf8c26.
D front-end changes: - Import dmd v2.099.0-rc.1. - The `main' can now return type `noreturn' and supports return inference. D Runtime changes: - Import druntime v2.099.0-rc.1. - C bindings for stat_t on powerpc-linux has been fixed. Phobos changes: - Import phobos v2.099.0-rc.1. gcc/d/ChangeLog: * d-target.cc (Target::_init): Initialize C type size fields. * dmd/MERGE: Merge upstream dmd cf63dd8e5. * dmd/VERSION: Update version to v2.099.0-rc.1. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime caf14b0f. * src/MERGE: Merge upstream phobos 41aaf8c26. gcc/testsuite/ChangeLog: * gdc.dg/torture/simd7413a.d: Update. * gdc.dg/ubsan/pr88957.d: Update. * gdc.dg/simd18489.d: New test. * gdc.dg/torture/simd21727.d: New test.
Diffstat (limited to 'libphobos')
-rw-r--r--libphobos/libdruntime/MERGE2
-rw-r--r--libphobos/libdruntime/core/gc/gcinterface.d4
-rw-r--r--libphobos/libdruntime/core/internal/gc/bits.d12
-rw-r--r--libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d257
-rw-r--r--libphobos/libdruntime/core/internal/gc/pooltable.d29
-rw-r--r--libphobos/libdruntime/core/internal/gc/proxy.d4
-rw-r--r--libphobos/libdruntime/core/memory.d4
-rw-r--r--libphobos/libdruntime/core/stdcpp/string.d8
-rw-r--r--libphobos/libdruntime/core/sys/posix/sys/stat.d85
-rw-r--r--libphobos/libdruntime/core/time.d158
-rw-r--r--libphobos/libdruntime/object.d13
-rw-r--r--libphobos/src/MERGE2
-rw-r--r--libphobos/src/std/file.d4
-rw-r--r--libphobos/src/std/getopt.d8
-rw-r--r--libphobos/src/std/range/primitives.d11
-rw-r--r--libphobos/src/std/sumtype.d108
16 files changed, 439 insertions, 270 deletions
diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE
index 49f6ae282e0..7c0bb573e2b 100644
--- a/libphobos/libdruntime/MERGE
+++ b/libphobos/libdruntime/MERGE
@@ -1,4 +1,4 @@
155528bd1e963d858eaa63901fc818b957c349fbc 1caf14b0f4ebbae4157aac89368d6278332ee2aa1
2 2
3The first line of this file holds the git revision number of the last 3The first line of this file holds the git revision number of the last
4merge done from the dlang/druntime repository. 4merge done from the dlang/druntime repository.
diff --git a/libphobos/libdruntime/core/gc/gcinterface.d b/libphobos/libdruntime/core/gc/gcinterface.d
index e8cdf1109ad..5560c6229ca 100644
--- a/libphobos/libdruntime/core/gc/gcinterface.d
+++ b/libphobos/libdruntime/core/gc/gcinterface.d
@@ -141,13 +141,13 @@ interface GC
141 * Retrieve statistics about garbage collection. 141 * Retrieve statistics about garbage collection.
142 * Useful for debugging and tuning. 142 * Useful for debugging and tuning.
143 */ 143 */
144 core.memory.GC.Stats stats() nothrow; 144 core.memory.GC.Stats stats() @safe nothrow @nogc;
145 145
146 /** 146 /**
147 * Retrieve profile statistics about garbage collection. 147 * Retrieve profile statistics about garbage collection.
148 * Useful for debugging and tuning. 148 * Useful for debugging and tuning.
149 */ 149 */
150 core.memory.GC.ProfileStats profileStats() nothrow @safe; 150 core.memory.GC.ProfileStats profileStats() @safe nothrow @nogc;
151 151
152 /** 152 /**
153 * add p to list of roots 153 * add p to list of roots
diff --git a/libphobos/libdruntime/core/internal/gc/bits.d b/libphobos/libdruntime/core/internal/gc/bits.d
index d50c38f0d21..3c1bb543460 100644
--- a/libphobos/libdruntime/core/internal/gc/bits.d
+++ b/libphobos/libdruntime/core/internal/gc/bits.d
@@ -60,7 +60,7 @@ struct GCBits
60 onOutOfMemoryError(); 60 onOutOfMemoryError();
61 } 61 }
62 62
63 wordtype test(size_t i) const nothrow 63 wordtype test(size_t i) const scope @trusted pure nothrow @nogc
64 in 64 in
65 { 65 {
66 assert(i < nbits); 66 assert(i < nbits);
@@ -70,7 +70,7 @@ struct GCBits
70 return core.bitop.bt(data, i); 70 return core.bitop.bt(data, i);
71 } 71 }
72 72
73 int set(size_t i) nothrow 73 int set(size_t i) scope @trusted pure nothrow @nogc
74 in 74 in
75 { 75 {
76 assert(i < nbits); 76 assert(i < nbits);
@@ -80,7 +80,7 @@ struct GCBits
80 return core.bitop.bts(data, i); 80 return core.bitop.bts(data, i);
81 } 81 }
82 82
83 int clear(size_t i) nothrow 83 int clear(size_t i) scope @trusted pure nothrow @nogc
84 in 84 in
85 { 85 {
86 assert(i <= nbits); 86 assert(i <= nbits);
@@ -91,7 +91,7 @@ struct GCBits
91 } 91 }
92 92
93 // return non-zero if bit already set 93 // return non-zero if bit already set
94 size_t setLocked(size_t i) nothrow 94 size_t setLocked(size_t i) scope @trusted pure nothrow @nogc
95 { 95 {
96 version (GNU) 96 version (GNU)
97 { 97 {
@@ -112,7 +112,7 @@ struct GCBits
112 } 112 }
113 else version (D_InlineAsm_X86) 113 else version (D_InlineAsm_X86)
114 { 114 {
115 asm @nogc nothrow { 115 asm pure @nogc nothrow {
116 mov EAX, this; 116 mov EAX, this;
117 mov ECX, data[EAX]; 117 mov ECX, data[EAX];
118 mov EDX, i; 118 mov EDX, i;
@@ -123,7 +123,7 @@ struct GCBits
123 } 123 }
124 else version (D_InlineAsm_X86_64) 124 else version (D_InlineAsm_X86_64)
125 { 125 {
126 asm @nogc nothrow { 126 asm pure @nogc nothrow {
127 mov RAX, this; 127 mov RAX, this;
128 mov RAX, data[RAX]; 128 mov RAX, data[RAX];
129 mov RDX, i; 129 mov RDX, i;
diff --git a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
index 87c45fb2872..aa51867fc2c 100644
--- a/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
+++ b/libphobos/libdruntime/core/internal/gc/impl/conservative/gc.d
@@ -38,6 +38,8 @@ import core.gc.config;
38import core.gc.gcinterface; 38import core.gc.gcinterface;
39 39
40import core.internal.container.treap; 40import core.internal.container.treap;
41import core.internal.spinlock;
42import core.internal.gc.pooltable;
41 43
42import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; 44import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;
43import core.stdc.string : memcpy, memset, memmove; 45import core.stdc.string : memcpy, memset, memmove;
@@ -145,7 +147,6 @@ class ConservativeGC : GC
145 147
146 Gcx *gcx; // implementation 148 Gcx *gcx; // implementation
147 149
148 import core.internal.spinlock;
149 static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy); 150 static gcLock = shared(AlignedSpinLock)(SpinLock.Contention.lengthy);
150 static bool _inFinalizer; 151 static bool _inFinalizer;
151 __gshared bool isPrecise = false; 152 __gshared bool isPrecise = false;
@@ -155,7 +156,7 @@ class ConservativeGC : GC
155 * 156 *
156 * Throws: InvalidMemoryOperationError on recursive locking during finalization. 157 * Throws: InvalidMemoryOperationError on recursive locking during finalization.
157 */ 158 */
158 static void lockNR() @nogc nothrow 159 static void lockNR() @safe @nogc nothrow
159 { 160 {
160 if (_inFinalizer) 161 if (_inFinalizer)
161 onInvalidMemoryOperationError(); 162 onInvalidMemoryOperationError();
@@ -685,7 +686,7 @@ class ConservativeGC : GC
685 else if (pagenum + newsz <= pool.npages) 686 else if (pagenum + newsz <= pool.npages)
686 { 687 {
687 // Attempt to expand in place (TODO: merge with extend) 688 // Attempt to expand in place (TODO: merge with extend)
688 if (lpool.pagetable[pagenum + psz] != B_FREE) 689 if (lpool.pagetable[pagenum + psz] != Bins.B_FREE)
689 return doMalloc(); 690 return doMalloc();
690 691
691 auto newPages = newsz - psz; 692 auto newPages = newsz - psz;
@@ -695,7 +696,7 @@ class ConservativeGC : GC
695 696
696 debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize); 697 debug (MEMSTOMP) memset(p + psize, 0xF0, size - psize);
697 debug (PRINTF) printFreeInfo(pool); 698 debug (PRINTF) printFreeInfo(pool);
698 memset(&lpool.pagetable[pagenum + psz], B_PAGEPLUS, newPages); 699 memset(&lpool.pagetable[pagenum + psz], Bins.B_PAGEPLUS, newPages);
699 lpool.bPageOffsets[pagenum] = cast(uint) newsz; 700 lpool.bPageOffsets[pagenum] = cast(uint) newsz;
700 for (auto offset = psz; offset < newsz; offset++) 701 for (auto offset = psz; offset < newsz; offset++)
701 lpool.bPageOffsets[pagenum + offset] = cast(uint) offset; 702 lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
@@ -766,7 +767,7 @@ class ConservativeGC : GC
766 767
767 auto lpool = cast(LargeObjectPool*) pool; 768 auto lpool = cast(LargeObjectPool*) pool;
768 size_t pagenum = lpool.pagenumOf(p); 769 size_t pagenum = lpool.pagenumOf(p);
769 if (lpool.pagetable[pagenum] != B_PAGE) 770 if (lpool.pagetable[pagenum] != Bins.B_PAGE)
770 return 0; 771 return 0;
771 772
772 size_t psz = lpool.bPageOffsets[pagenum]; 773 size_t psz = lpool.bPageOffsets[pagenum];
@@ -777,7 +778,7 @@ class ConservativeGC : GC
777 778
778 if (pagenum + psz >= lpool.npages) 779 if (pagenum + psz >= lpool.npages)
779 return 0; 780 return 0;
780 if (lpool.pagetable[pagenum + psz] != B_FREE) 781 if (lpool.pagetable[pagenum + psz] != Bins.B_FREE)
781 return 0; 782 return 0;
782 783
783 size_t freesz = lpool.bPageOffsets[pagenum + psz]; 784 size_t freesz = lpool.bPageOffsets[pagenum + psz];
@@ -785,7 +786,7 @@ class ConservativeGC : GC
785 return 0; 786 return 0;
786 size_t sz = freesz > maxsz ? maxsz : freesz; 787 size_t sz = freesz > maxsz ? maxsz : freesz;
787 debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE); 788 debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE);
788 memset(lpool.pagetable + pagenum + psz, B_PAGEPLUS, sz); 789 memset(lpool.pagetable + pagenum + psz, Bins.B_PAGEPLUS, sz);
789 lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz); 790 lpool.bPageOffsets[pagenum] = cast(uint) (psz + sz);
790 for (auto offset = psz; offset < psz + sz; offset++) 791 for (auto offset = psz; offset < psz + sz; offset++)
791 lpool.bPageOffsets[pagenum + offset] = cast(uint) offset; 792 lpool.bPageOffsets[pagenum + offset] = cast(uint) offset;
@@ -874,11 +875,11 @@ class ConservativeGC : GC
874 debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]); 875 debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]);
875 debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]); 876 debug(PRINTF) if (pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]);
876 877
877 bin = cast(Bins)pool.pagetable[pagenum]; 878 bin = pool.pagetable[pagenum];
878 879
879 // Verify that the pointer is at the beginning of a block, 880 // Verify that the pointer is at the beginning of a block,
880 // no action should be taken if p is an interior pointer 881 // no action should be taken if p is an interior pointer
881 if (bin > B_PAGE) // B_PAGEPLUS or B_FREE 882 if (bin > Bins.B_PAGE) // B_PAGEPLUS or B_FREE
882 return; 883 return;
883 size_t off = (sentinel_sub(p) - pool.baseAddr); 884 size_t off = (sentinel_sub(p) - pool.baseAddr);
884 size_t base = baseOffset(off, bin); 885 size_t base = baseOffset(off, bin);
@@ -893,7 +894,7 @@ class ConservativeGC : GC
893 if (pool.isLargeObject) // if large alloc 894 if (pool.isLargeObject) // if large alloc
894 { 895 {
895 biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large; 896 biti = cast(size_t)(p - pool.baseAddr) >> pool.ShiftBy.Large;
896 assert(bin == B_PAGE); 897 assert(bin == Bins.B_PAGE);
897 auto lpool = cast(LargeObjectPool*) pool; 898 auto lpool = cast(LargeObjectPool*) pool;
898 899
899 // Free pages 900 // Free pages
@@ -1094,13 +1095,13 @@ class ConservativeGC : GC
1094 pool = gcx.findPool(p); 1095 pool = gcx.findPool(p);
1095 assert(pool); 1096 assert(pool);
1096 pagenum = pool.pagenumOf(p); 1097 pagenum = pool.pagenumOf(p);
1097 bin = cast(Bins)pool.pagetable[pagenum]; 1098 bin = pool.pagetable[pagenum];
1098 assert(bin <= B_PAGE); 1099 assert(bin <= Bins.B_PAGE);
1099 assert(p == cast(void*)baseOffset(cast(size_t)p, bin)); 1100 assert(p == cast(void*)baseOffset(cast(size_t)p, bin));
1100 1101
1101 debug (PTRCHECK2) 1102 debug (PTRCHECK2)
1102 { 1103 {
1103 if (bin < B_PAGE) 1104 if (bin < Bins.B_PAGE)
1104 { 1105 {
1105 // Check that p is not on a free list 1106 // Check that p is not on a free list
1106 List *list; 1107 List *list;
@@ -1299,7 +1300,7 @@ class ConservativeGC : GC
1299 } 1300 }
1300 1301
1301 1302
1302 core.memory.GC.Stats stats() nothrow 1303 core.memory.GC.Stats stats() @safe nothrow @nogc
1303 { 1304 {
1304 typeof(return) ret; 1305 typeof(return) ret;
1305 1306
@@ -1332,13 +1333,20 @@ class ConservativeGC : GC
1332 // 1333 //
1333 // Implementation of getStats 1334 // Implementation of getStats
1334 // 1335 //
1335 private void getStatsNoSync(out core.memory.GC.Stats stats) nothrow 1336 private void getStatsNoSync(out core.memory.GC.Stats stats) @trusted nothrow @nogc
1336 { 1337 {
1337 foreach (pool; gcx.pooltable[0 .. gcx.npools]) 1338 // This function is trusted for two reasons: `pool.pagetable` is a pointer,
1339 // which is being sliced in the below foreach, and so is `binPageChain`,
1340 // also sliced a bit later in this function.
1341 // However, both usages are safe as long as the assumption that `npools`
1342 // defines the limit for `pagetable`'s length holds true (see allocation).
1343 // The slicing happens at __LINE__ + 4 and __LINE__ + 24.
1344 // `@trusted` delegates are not used to prevent any performance issue.
1345 foreach (pool; gcx.pooltable[])
1338 { 1346 {
1339 foreach (bin; pool.pagetable[0 .. pool.npages]) 1347 foreach (bin; pool.pagetable[0 .. pool.npages])
1340 { 1348 {
1341 if (bin == B_FREE) 1349 if (bin == Bins.B_FREE)
1342 stats.freeSize += PAGESIZE; 1350 stats.freeSize += PAGESIZE;
1343 else 1351 else
1344 stats.usedSize += PAGESIZE; 1352 stats.usedSize += PAGESIZE;
@@ -1346,13 +1354,13 @@ class ConservativeGC : GC
1346 } 1354 }
1347 1355
1348 size_t freeListSize; 1356 size_t freeListSize;
1349 foreach (n; 0 .. B_PAGE) 1357 foreach (n; 0 .. Bins.B_PAGE)
1350 { 1358 {
1351 immutable sz = binsize[n]; 1359 immutable sz = binsize[n];
1352 for (List *list = gcx.bucket[n]; list; list = list.next) 1360 for (List *list = gcx.bucket[n]; list; list = list.next)
1353 freeListSize += sz; 1361 freeListSize += sz;
1354 1362
1355 foreach (pool; gcx.pooltable[0 .. gcx.npools]) 1363 foreach (pool; gcx.pooltable[])
1356 { 1364 {
1357 if (pool.isLargeObject) 1365 if (pool.isLargeObject)
1358 continue; 1366 continue;
@@ -1381,7 +1389,7 @@ enum
1381} 1389}
1382 1390
1383 1391
1384enum 1392enum Bins : ubyte
1385{ 1393{
1386 B_16, 1394 B_16,
1387 B_32, 1395 B_32,
@@ -1405,10 +1413,6 @@ enum
1405 B_MAX, 1413 B_MAX,
1406} 1414}
1407 1415
1408
1409alias ubyte Bins;
1410
1411
1412struct List 1416struct List
1413{ 1417{
1414 List *next; 1418 List *next;
@@ -1416,12 +1420,12 @@ struct List
1416} 1420}
1417 1421
1418// non power of two sizes optimized for small remainder within page (<= 64 bytes) 1422// non power of two sizes optimized for small remainder within page (<= 64 bytes)
1419immutable short[B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ]; 1423immutable short[Bins.B_NUMSMALL + 1] binsize = [ 16, 32, 48, 64, 96, 128, 176, 256, 368, 512, 816, 1024, 1360, 2048, 4096 ];
1420immutable short[PAGESIZE / 16][B_NUMSMALL + 1] binbase = calcBinBase(); 1424immutable short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] binbase = calcBinBase();
1421 1425
1422short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase() 1426short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] calcBinBase()
1423{ 1427{
1424 short[PAGESIZE / 16][B_NUMSMALL + 1] bin; 1428 short[PAGESIZE / 16][Bins.B_NUMSMALL + 1] bin;
1425 1429
1426 foreach (i, size; binsize) 1430 foreach (i, size; binsize)
1427 { 1431 {
@@ -1440,7 +1444,7 @@ short[PAGESIZE / 16][B_NUMSMALL + 1] calcBinBase()
1440 1444
1441size_t baseOffset(size_t offset, Bins bin) @nogc nothrow 1445size_t baseOffset(size_t offset, Bins bin) @nogc nothrow
1442{ 1446{
1443 assert(bin <= B_PAGE); 1447 assert(bin <= Bins.B_PAGE);
1444 return (offset & ~(PAGESIZE - 1)) + binbase[bin][(offset & (PAGESIZE - 1)) >> 4]; 1448 return (offset & ~(PAGESIZE - 1)) + binbase[bin][(offset & (PAGESIZE - 1)) >> 4];
1445} 1449}
1446 1450
@@ -1448,9 +1452,9 @@ alias PageBits = GCBits.wordtype[PAGESIZE / 16 / GCBits.BITS_PER_WORD];
1448static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0); 1452static assert(PAGESIZE % (GCBits.BITS_PER_WORD * 16) == 0);
1449 1453
1450// bitmask with bits set at base offsets of objects 1454// bitmask with bits set at base offsets of objects
1451immutable PageBits[B_NUMSMALL] baseOffsetBits = (){ 1455immutable PageBits[Bins.B_NUMSMALL] baseOffsetBits = (){
1452 PageBits[B_NUMSMALL] bits; 1456 PageBits[Bins.B_NUMSMALL] bits;
1453 foreach (bin; 0..B_NUMSMALL) 1457 foreach (bin; 0 .. Bins.B_NUMSMALL)
1454 { 1458 {
1455 size_t size = binsize[bin]; 1459 size_t size = binsize[bin];
1456 const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned 1460 const top = PAGESIZE - size + 1; // ensure <size> bytes available even if unaligned
@@ -1475,7 +1479,6 @@ private void set(ref PageBits bits, size_t i) @nogc pure nothrow
1475 1479
1476struct Gcx 1480struct Gcx
1477{ 1481{
1478 import core.internal.spinlock;
1479 auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); 1482 auto rootsLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
1480 auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief); 1483 auto rangesLock = shared(AlignedSpinLock)(SpinLock.Contention.brief);
1481 Treap!Root roots; 1484 Treap!Root roots;
@@ -1491,11 +1494,9 @@ struct Gcx
1491 debug(INVARIANT) bool inCollection; 1494 debug(INVARIANT) bool inCollection;
1492 uint disabled; // turn off collections if >0 1495 uint disabled; // turn off collections if >0
1493 1496
1494 import core.internal.gc.pooltable;
1495 private @property size_t npools() pure const nothrow { return pooltable.length; }
1496 PoolTable!Pool pooltable; 1497 PoolTable!Pool pooltable;
1497 1498
1498 List*[B_NUMSMALL] bucket; // free list for each small size 1499 List*[Bins.B_NUMSMALL] bucket; // free list for each small size
1499 1500
1500 // run a collection when reaching those thresholds (number of used pages) 1501 // run a collection when reaching those thresholds (number of used pages)
1501 float smallCollectThreshold, largeCollectThreshold; 1502 float smallCollectThreshold, largeCollectThreshold;
@@ -1508,7 +1509,7 @@ struct Gcx
1508 else 1509 else
1509 alias leakDetector = LeakDetector; 1510 alias leakDetector = LeakDetector;
1510 1511
1511 SmallObjectPool*[B_NUMSMALL] recoverPool; 1512 SmallObjectPool*[Bins.B_NUMSMALL] recoverPool;
1512 version (Posix) __gshared Gcx* instance; 1513 version (Posix) __gshared Gcx* instance;
1513 1514
1514 void initialize() 1515 void initialize()
@@ -1592,9 +1593,8 @@ struct Gcx
1592 1593
1593 debug(INVARIANT) initialized = false; 1594 debug(INVARIANT) initialized = false;
1594 1595
1595 for (size_t i = 0; i < npools; i++) 1596 foreach (Pool* pool; this.pooltable[])
1596 { 1597 {
1597 Pool *pool = pooltable[i];
1598 mappedPages -= pool.npages; 1598 mappedPages -= pool.npages;
1599 pool.Dtor(); 1599 pool.Dtor();
1600 cstdlib.free(pool); 1600 cstdlib.free(pool);
@@ -1635,7 +1635,7 @@ struct Gcx
1635 if (!inCollection) 1635 if (!inCollection)
1636 (cast()rangesLock).unlock(); 1636 (cast()rangesLock).unlock();
1637 1637
1638 for (size_t i = 0; i < B_NUMSMALL; i++) 1638 for (size_t i = 0; i < Bins.B_NUMSMALL; i++)
1639 { 1639 {
1640 size_t j = 0; 1640 size_t j = 0;
1641 List* prev, pprev, ppprev; // keep a short history to inspect in the debugger 1641 List* prev, pprev, ppprev; // keep a short history to inspect in the debugger
@@ -1752,7 +1752,7 @@ struct Gcx
1752 ConservativeGC._inFinalizer = true; 1752 ConservativeGC._inFinalizer = true;
1753 scope (failure) ConservativeGC._inFinalizer = false; 1753 scope (failure) ConservativeGC._inFinalizer = false;
1754 1754
1755 foreach (pool; pooltable[0 .. npools]) 1755 foreach (pool; this.pooltable[])
1756 { 1756 {
1757 if (!pool.finals.nbits) continue; 1757 if (!pool.finals.nbits) continue;
1758 1758
@@ -1816,18 +1816,18 @@ struct Gcx
1816 /** 1816 /**
1817 * Computes the bin table using CTFE. 1817 * Computes the bin table using CTFE.
1818 */ 1818 */
1819 static byte[2049] ctfeBins() nothrow 1819 static Bins[2049] ctfeBins() nothrow
1820 { 1820 {
1821 byte[2049] ret; 1821 Bins[2049] ret;
1822 size_t p = 0; 1822 size_t p = 0;
1823 for (Bins b = B_16; b <= B_2048; b++) 1823 for (Bins b = Bins.B_16; b <= Bins.B_2048; b++)
1824 for ( ; p <= binsize[b]; p++) 1824 for ( ; p <= binsize[b]; p++)
1825 ret[p] = b; 1825 ret[p] = b;
1826 1826
1827 return ret; 1827 return ret;
1828 } 1828 }
1829 1829
1830 static const byte[2049] binTable = ctfeBins(); 1830 static immutable Bins[2049] binTable = ctfeBins();
1831 1831
1832 /** 1832 /**
1833 * Allocate a new pool of at least size bytes. 1833 * Allocate a new pool of at least size bytes.
@@ -1994,7 +1994,7 @@ struct Gcx
1994 1994
1995 bool tryAlloc() nothrow 1995 bool tryAlloc() nothrow
1996 { 1996 {
1997 foreach (p; pooltable[0 .. npools]) 1997 foreach (p; this.pooltable[])
1998 { 1998 {
1999 if (!p.isLargeObject || p.freepages < npages) 1999 if (!p.isLargeObject || p.freepages < npages)
2000 continue; 2000 continue;
@@ -2094,10 +2094,11 @@ struct Gcx
2094 } 2094 }
2095 2095
2096 // Allocate successively larger pools up to 8 megs 2096 // Allocate successively larger pools up to 8 megs
2097 if (npools) 2097 if (this.pooltable.length)
2098 { size_t n; 2098 {
2099 size_t n;
2099 2100
2100 n = config.minPoolSize + config.incPoolSize * npools; 2101 n = config.minPoolSize + config.incPoolSize * this.pooltable.length;
2101 if (n > config.maxPoolSize) 2102 if (n > config.maxPoolSize)
2102 n = config.maxPoolSize; // cap pool size 2103 n = config.maxPoolSize; // cap pool size
2103 n /= PAGESIZE; // convert bytes to pages 2104 n /= PAGESIZE; // convert bytes to pages
@@ -2139,9 +2140,8 @@ struct Gcx
2139 List* allocPage(Bins bin) nothrow 2140 List* allocPage(Bins bin) nothrow
2140 { 2141 {
2141 //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin); 2142 //debug(PRINTF) printf("Gcx::allocPage(bin = %d)\n", bin);
2142 for (size_t n = 0; n < npools; n++) 2143 foreach (Pool* pool; this.pooltable[])
2143 { 2144 {
2144 Pool* pool = pooltable[n];
2145 if (pool.isLargeObject) 2145 if (pool.isLargeObject)
2146 continue; 2146 continue;
2147 if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin)) 2147 if (List* p = (cast(SmallObjectPool*)pool).allocPage(bin))
@@ -2275,7 +2275,7 @@ struct Gcx
2275 2275
2276 // let dmd allocate a register for this.pools 2276 // let dmd allocate a register for this.pools
2277 auto pools = pooltable.pools; 2277 auto pools = pooltable.pools;
2278 const highpool = pooltable.npools - 1; 2278 const highpool = pooltable.length - 1;
2279 const minAddr = pooltable.minAddr; 2279 const minAddr = pooltable.minAddr;
2280 size_t memSize = pooltable.maxAddr - minAddr; 2280 size_t memSize = pooltable.maxAddr - minAddr;
2281 Pool* pool = null; 2281 Pool* pool = null;
@@ -2300,7 +2300,6 @@ struct Gcx
2300 bitpos -= rng.bmplength; 2300 bitpos -= rng.bmplength;
2301 rng.pbase += rng.bmplength; 2301 rng.pbase += rng.bmplength;
2302 } 2302 }
2303 import core.bitop;
2304 if (!core.bitop.bt(rng.ptrbmp, bitpos)) 2303 if (!core.bitop.bt(rng.ptrbmp, bitpos))
2305 { 2304 {
2306 debug(MARK_PRINTF) printf("\t\tskipping non-pointer\n"); 2305 debug(MARK_PRINTF) printf("\t\tskipping non-pointer\n");
@@ -2335,7 +2334,7 @@ struct Gcx
2335 printf("\t\tfound pool %p, base=%p, pn = %lld, bin = %d\n", pool, pool.baseAddr, cast(long)pn, bin); 2334 printf("\t\tfound pool %p, base=%p, pn = %lld, bin = %d\n", pool, pool.baseAddr, cast(long)pn, bin);
2336 2335
2337 // Adjust bit to be at start of allocated memory block 2336 // Adjust bit to be at start of allocated memory block
2338 if (bin < B_PAGE) 2337 if (bin < Bins.B_PAGE)
2339 { 2338 {
2340 // We don't care abou setting pointsToBase correctly 2339 // We don't care abou setting pointsToBase correctly
2341 // because it's ignored for small object pools anyhow. 2340 // because it's ignored for small object pools anyhow.
@@ -2356,7 +2355,7 @@ struct Gcx
2356 goto LaddRange; 2355 goto LaddRange;
2357 } 2356 }
2358 } 2357 }
2359 else if (bin == B_PAGE) 2358 else if (bin == Bins.B_PAGE)
2360 { 2359 {
2361 biti = offset >> Pool.ShiftBy.Large; 2360 biti = offset >> Pool.ShiftBy.Large;
2362 //debug(PRINTF) printf("\t\tbiti = x%x\n", biti); 2361 //debug(PRINTF) printf("\t\tbiti = x%x\n", biti);
@@ -2376,7 +2375,7 @@ struct Gcx
2376 goto LaddLargeRange; 2375 goto LaddLargeRange;
2377 } 2376 }
2378 } 2377 }
2379 else if (bin == B_PAGEPLUS) 2378 else if (bin == Bins.B_PAGEPLUS)
2380 { 2379 {
2381 pn -= pool.bPageOffsets[pn]; 2380 pn -= pool.bPageOffsets[pn];
2382 biti = pn * (PAGESIZE >> Pool.ShiftBy.Large); 2381 biti = pn * (PAGESIZE >> Pool.ShiftBy.Large);
@@ -2429,7 +2428,7 @@ struct Gcx
2429 else 2428 else
2430 { 2429 {
2431 // Don't mark bits in B_FREE pages 2430 // Don't mark bits in B_FREE pages
2432 assert(bin == B_FREE); 2431 assert(bin == Bins.B_FREE);
2433 } 2432 }
2434 } 2433 }
2435 LnextPtr: 2434 LnextPtr:
@@ -2526,9 +2525,8 @@ struct Gcx
2526 { 2525 {
2527 debug(COLLECT_PRINTF) printf("preparing mark.\n"); 2526 debug(COLLECT_PRINTF) printf("preparing mark.\n");
2528 2527
2529 for (size_t n = 0; n < npools; n++) 2528 foreach (Pool* pool; this.pooltable[])
2530 { 2529 {
2531 Pool* pool = pooltable[n];
2532 if (pool.isLargeObject) 2530 if (pool.isLargeObject)
2533 pool.mark.zero(); 2531 pool.mark.zero();
2534 else 2532 else
@@ -2598,10 +2596,9 @@ struct Gcx
2598 size_t freedLargePages; 2596 size_t freedLargePages;
2599 size_t freedSmallPages; 2597 size_t freedSmallPages;
2600 size_t freed; 2598 size_t freed;
2601 for (size_t n = 0; n < npools; n++) 2599 foreach (Pool* pool; this.pooltable[])
2602 { 2600 {
2603 size_t pn; 2601 size_t pn;
2604 Pool* pool = pooltable[n];
2605 2602
2606 if (pool.isLargeObject) 2603 if (pool.isLargeObject)
2607 { 2604 {
@@ -2612,12 +2609,12 @@ struct Gcx
2612 { 2609 {
2613 npages = pool.bPageOffsets[pn]; 2610 npages = pool.bPageOffsets[pn];
2614 Bins bin = cast(Bins)pool.pagetable[pn]; 2611 Bins bin = cast(Bins)pool.pagetable[pn];
2615 if (bin == B_FREE) 2612 if (bin == Bins.B_FREE)
2616 { 2613 {
2617 numFree += npages; 2614 numFree += npages;
2618 continue; 2615 continue;
2619 } 2616 }
2620 assert(bin == B_PAGE); 2617 assert(bin == Bins.B_PAGE);
2621 size_t biti = pn; 2618 size_t biti = pn;
2622 2619
2623 if (!pool.mark.test(biti)) 2620 if (!pool.mark.test(biti))
@@ -2637,7 +2634,7 @@ struct Gcx
2637 2634
2638 debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p); 2635 debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
2639 leakDetector.log_free(q, sentinel_size(q, npages * PAGESIZE - SENTINEL_EXTRA)); 2636 leakDetector.log_free(q, sentinel_size(q, npages * PAGESIZE - SENTINEL_EXTRA));
2640 pool.pagetable[pn..pn+npages] = B_FREE; 2637 pool.pagetable[pn..pn+npages] = Bins.B_FREE;
2641 if (pn < pool.searchStart) pool.searchStart = pn; 2638 if (pn < pool.searchStart) pool.searchStart = pn;
2642 freedLargePages += npages; 2639 freedLargePages += npages;
2643 pool.freepages += npages; 2640 pool.freepages += npages;
@@ -2671,7 +2668,7 @@ struct Gcx
2671 { 2668 {
2672 Bins bin = cast(Bins)pool.pagetable[pn]; 2669 Bins bin = cast(Bins)pool.pagetable[pn];
2673 2670
2674 if (bin < B_PAGE) 2671 if (bin < Bins.B_PAGE)
2675 { 2672 {
2676 auto freebitsdata = pool.freebits.data + pn * PageBits.length; 2673 auto freebitsdata = pool.freebits.data + pn * PageBits.length;
2677 auto markdata = pool.mark.data + pn * PageBits.length; 2674 auto markdata = pool.mark.data + pn * PageBits.length;
@@ -2767,7 +2764,7 @@ struct Gcx
2767 { 2764 {
2768 pool.freeAllPageBits(pn); 2765 pool.freeAllPageBits(pn);
2769 2766
2770 pool.pagetable[pn] = B_FREE; 2767 pool.pagetable[pn] = Bins.B_FREE;
2771 // add to free chain 2768 // add to free chain
2772 pool.binPageChain[pn] = cast(uint) pool.searchStart; 2769 pool.binPageChain[pn] = cast(uint) pool.searchStart;
2773 pool.searchStart = pn; 2770 pool.searchStart = pn;
@@ -2789,7 +2786,8 @@ struct Gcx
2789 2786
2790 assert(freedLargePages <= usedLargePages); 2787 assert(freedLargePages <= usedLargePages);
2791 usedLargePages -= freedLargePages; 2788 usedLargePages -= freedLargePages;
2792 debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedLargePages, npools); 2789 debug(COLLECT_PRINTF) printf("\tfree'd %u bytes, %u pages from %u pools\n",
2790 freed, freedLargePages, this.pooltable.length);
2793 2791
2794 assert(freedSmallPages <= usedSmallPages); 2792 assert(freedSmallPages <= usedSmallPages);
2795 usedSmallPages -= freedSmallPages; 2793 usedSmallPages -= freedSmallPages;
@@ -2854,12 +2852,12 @@ struct Gcx
2854 private SmallObjectPool* setNextRecoverPool(Bins bin, size_t poolIndex) nothrow 2852 private SmallObjectPool* setNextRecoverPool(Bins bin, size_t poolIndex) nothrow
2855 { 2853 {
2856 Pool* pool; 2854 Pool* pool;
2857 while (poolIndex < npools && 2855 while (poolIndex < this.pooltable.length &&
2858 ((pool = pooltable[poolIndex]).isLargeObject || 2856 ((pool = this.pooltable[poolIndex]).isLargeObject ||
2859 pool.recoverPageFirst[bin] >= pool.npages)) 2857 pool.recoverPageFirst[bin] >= pool.npages))
2860 poolIndex++; 2858 poolIndex++;
2861 2859
2862 return recoverPool[bin] = poolIndex < npools ? cast(SmallObjectPool*)pool : null; 2860 return recoverPool[bin] = poolIndex < this.pooltable.length ? cast(SmallObjectPool*)pool : null;
2863 } 2861 }
2864 2862
2865 version (COLLECT_FORK) 2863 version (COLLECT_FORK)
@@ -2928,7 +2926,6 @@ struct Gcx
2928 import core.stdc.stdlib : _Exit; 2926 import core.stdc.stdlib : _Exit;
2929 debug (PRINTF_TO_FILE) 2927 debug (PRINTF_TO_FILE)
2930 { 2928 {
2931 import core.stdc.stdio : fflush;
2932 fflush(null); // avoid duplicated FILE* output 2929 fflush(null); // avoid duplicated FILE* output
2933 } 2930 }
2934 version (OSX) 2931 version (OSX)
@@ -3153,7 +3150,7 @@ Lmark:
3153 3150
3154 // init bucket lists 3151 // init bucket lists
3155 bucket[] = null; 3152 bucket[] = null;
3156 foreach (Bins bin; 0..B_NUMSMALL) 3153 foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL)
3157 setNextRecoverPool(bin, 0); 3154 setNextRecoverPool(bin, 0);
3158 3155
3159 stop = currTime; 3156 stop = currTime;
@@ -3188,24 +3185,24 @@ Lmark:
3188 auto pn = offset / PAGESIZE; 3185 auto pn = offset / PAGESIZE;
3189 auto bins = cast(Bins)pool.pagetable[pn]; 3186 auto bins = cast(Bins)pool.pagetable[pn];
3190 size_t biti = void; 3187 size_t biti = void;
3191 if (bins < B_PAGE) 3188 if (bins < Bins.B_PAGE)
3192 { 3189 {
3193 biti = baseOffset(offset, bins) >> pool.ShiftBy.Small; 3190 biti = baseOffset(offset, bins) >> pool.ShiftBy.Small;
3194 // doesn't need to check freebits because no pointer must exist 3191 // doesn't need to check freebits because no pointer must exist
3195 // to a block that was free before starting the collection 3192 // to a block that was free before starting the collection
3196 } 3193 }
3197 else if (bins == B_PAGE) 3194 else if (bins == Bins.B_PAGE)
3198 { 3195 {
3199 biti = pn * (PAGESIZE >> pool.ShiftBy.Large); 3196 biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
3200 } 3197 }
3201 else if (bins == B_PAGEPLUS) 3198 else if (bins == Bins.B_PAGEPLUS)
3202 { 3199 {
3203 pn -= pool.bPageOffsets[pn]; 3200 pn -= pool.bPageOffsets[pn];
3204 biti = pn * (PAGESIZE >> pool.ShiftBy.Large); 3201 biti = pn * (PAGESIZE >> pool.ShiftBy.Large);
3205 } 3202 }
3206 else // bins == B_FREE 3203 else // bins == Bins.B_FREE
3207 { 3204 {
3208 assert(bins == B_FREE); 3205 assert(bins == Bins.B_FREE);
3209 return IsMarked.no; 3206 return IsMarked.no;
3210 } 3207 }
3211 return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no; 3208 return pool.mark.test(biti) ? IsMarked.yes : IsMarked.no;
@@ -3262,8 +3259,11 @@ Lmark:
3262 3259
3263 /* ============================ Parallel scanning =============================== */ 3260 /* ============================ Parallel scanning =============================== */
3264 version (COLLECT_PARALLEL): 3261 version (COLLECT_PARALLEL):
3265 import core.sync.event; 3262
3266 import core.atomic; 3263 import core.atomic;
3264 import core.cpuid;
3265 import core.sync.event;
3266
3267 private: // disable invariants for background threads 3267 private: // disable invariants for background threads
3268 3268
3269 static struct ScanThreadData 3269 static struct ScanThreadData
@@ -3334,7 +3334,6 @@ Lmark:
3334 3334
3335 int maxParallelThreads() nothrow 3335 int maxParallelThreads() nothrow
3336 { 3336 {
3337 import core.cpuid;
3338 auto threads = threadsPerCPU(); 3337 auto threads = threadsPerCPU();
3339 3338
3340 if (threads == 0) 3339 if (threads == 0)
@@ -3512,7 +3511,7 @@ struct Pool
3512 GCBits is_pointer; // precise GC only: per-word, not per-block like the rest of them (SmallObjectPool only) 3511 GCBits is_pointer; // precise GC only: per-word, not per-block like the rest of them (SmallObjectPool only)
3513 size_t npages; 3512 size_t npages;
3514 size_t freepages; // The number of pages not in use. 3513 size_t freepages; // The number of pages not in use.
3515 ubyte* pagetable; 3514 Bins* pagetable;
3516 3515
3517 bool isLargeObject; 3516 bool isLargeObject;
3518 3517
@@ -3541,7 +3540,7 @@ struct Pool
3541 enum PageRecovered = uint.max; 3540 enum PageRecovered = uint.max;
3542 3541
3543 // first of chain of pages to recover (SmallObjectPool only) 3542 // first of chain of pages to recover (SmallObjectPool only)
3544 uint[B_NUMSMALL] recoverPageFirst; 3543 uint[Bins.B_NUMSMALL] recoverPageFirst;
3545 3544
3546 // precise GC: TypeInfo.rtInfo for allocation (LargeObjectPool only) 3545 // precise GC: TypeInfo.rtInfo for allocation (LargeObjectPool only)
3547 immutable(size_t)** rtinfo; 3546 immutable(size_t)** rtinfo;
@@ -3611,7 +3610,7 @@ struct Pool
3611 noscan.alloc(nbits); 3610 noscan.alloc(nbits);
3612 appendable.alloc(nbits); 3611 appendable.alloc(nbits);
3613 3612
3614 pagetable = cast(ubyte*)cstdlib.malloc(npages); 3613 pagetable = cast(Bins*)cstdlib.malloc(npages * Bins.sizeof);
3615 if (!pagetable) 3614 if (!pagetable)
3616 onOutOfMemoryErrorNoGC(); 3615 onOutOfMemoryErrorNoGC();
3617 3616
@@ -3635,7 +3634,7 @@ struct Pool
3635 } 3634 }
3636 } 3635 }
3637 3636
3638 memset(pagetable, B_FREE, npages); 3637 memset(pagetable, Bins.B_FREE, npages);
3639 3638
3640 this.npages = npages; 3639 this.npages = npages;
3641 this.freepages = npages; 3640 this.freepages = npages;
@@ -3852,7 +3851,7 @@ struct Pool
3852 } 3851 }
3853 3852
3854 public 3853 public
3855 @property bool isFree() const pure nothrow 3854 @property bool isFree() const scope @safe pure nothrow @nogc
3856 { 3855 {
3857 return npages == freepages; 3856 return npages == freepages;
3858 } 3857 }
@@ -3883,10 +3882,10 @@ struct Pool
3883 { 3882 {
3884 size_t offset = cast(size_t)(p - baseAddr); 3883 size_t offset = cast(size_t)(p - baseAddr);
3885 size_t pn = offset / PAGESIZE; 3884 size_t pn = offset / PAGESIZE;
3886 Bins bin = cast(Bins)pagetable[pn]; 3885 Bins bin = pagetable[pn];
3887 3886
3888 // Adjust bit to be at start of allocated memory block 3887 // Adjust bit to be at start of allocated memory block
3889 if (bin < B_NUMSMALL) 3888 if (bin < Bins.B_NUMSMALL)
3890 { 3889 {
3891 auto baseOff = baseOffset(offset, bin); 3890 auto baseOff = baseOffset(offset, bin);
3892 const biti = baseOff >> Pool.ShiftBy.Small; 3891 const biti = baseOff >> Pool.ShiftBy.Small;
@@ -3894,11 +3893,11 @@ struct Pool
3894 return null; 3893 return null;
3895 return baseAddr + baseOff; 3894 return baseAddr + baseOff;
3896 } 3895 }
3897 if (bin == B_PAGE) 3896 if (bin == Bins.B_PAGE)
3898 { 3897 {
3899 return baseAddr + (offset & (offset.max ^ (PAGESIZE-1))); 3898 return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
3900 } 3899 }
3901 if (bin == B_PAGEPLUS) 3900 if (bin == Bins.B_PAGEPLUS)
3902 { 3901 {
3903 size_t pageOffset = bPageOffsets[pn]; 3902 size_t pageOffset = bPageOffsets[pn];
3904 offset -= pageOffset * PAGESIZE; 3903 offset -= pageOffset * PAGESIZE;
@@ -3907,7 +3906,7 @@ struct Pool
3907 return baseAddr + (offset & (offset.max ^ (PAGESIZE-1))); 3906 return baseAddr + (offset & (offset.max ^ (PAGESIZE-1)));
3908 } 3907 }
3909 // we are in a B_FREE page 3908 // we are in a B_FREE page
3910 assert(bin == B_FREE); 3909 assert(bin == Bins.B_FREE);
3911 return null; 3910 return null;
3912 } 3911 }
3913 3912
@@ -3944,8 +3943,8 @@ struct Pool
3944 { 3943 {
3945 for (size_t i = 0; i < npages; i++) 3944 for (size_t i = 0; i < npages; i++)
3946 { 3945 {
3947 Bins bin = cast(Bins)pagetable[i]; 3946 Bins bin = pagetable[i];
3948 assert(bin < B_MAX); 3947 assert(bin < Bins.B_MAX);
3949 } 3948 }
3950 } 3949 }
3951 } 3950 }
@@ -4053,19 +4052,19 @@ struct LargeObjectPool
4053 uint np = bPageOffsets[n]; 4052 uint np = bPageOffsets[n];
4054 assert(np > 0 && np <= npages - n); 4053 assert(np > 0 && np <= npages - n);
4055 4054
4056 if (pagetable[n] == B_PAGE) 4055 if (pagetable[n] == Bins.B_PAGE)
4057 { 4056 {
4058 for (uint p = 1; p < np; p++) 4057 for (uint p = 1; p < np; p++)
4059 { 4058 {
4060 assert(pagetable[n + p] == B_PAGEPLUS); 4059 assert(pagetable[n + p] == Bins.B_PAGEPLUS);
4061 assert(bPageOffsets[n + p] == p); 4060 assert(bPageOffsets[n + p] == p);
4062 } 4061 }
4063 } 4062 }
4064 else if (pagetable[n] == B_FREE) 4063 else if (pagetable[n] == Bins.B_FREE)
4065 { 4064 {
4066 for (uint p = 1; p < np; p++) 4065 for (uint p = 1; p < np; p++)
4067 { 4066 {
4068 assert(pagetable[n + p] == B_FREE); 4067 assert(pagetable[n + p] == Bins.B_FREE);
4069 } 4068 }
4070 assert(bPageOffsets[n + np - 1] == np); 4069 assert(bPageOffsets[n + np - 1] == np);
4071 } 4070 }
@@ -4086,17 +4085,17 @@ struct LargeObjectPool
4086 4085
4087 //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n); 4086 //debug(PRINTF) printf("Pool::allocPages(n = %d)\n", n);
4088 size_t largest = 0; 4087 size_t largest = 0;
4089 if (pagetable[searchStart] == B_PAGEPLUS) 4088 if (pagetable[searchStart] == Bins.B_PAGEPLUS)
4090 { 4089 {
4091 searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE 4090 searchStart -= bPageOffsets[searchStart]; // jump to B_PAGE
4092 searchStart += bPageOffsets[searchStart]; 4091 searchStart += bPageOffsets[searchStart];
4093 } 4092 }
4094 while (searchStart < npages && pagetable[searchStart] == B_PAGE) 4093 while (searchStart < npages && pagetable[searchStart] == Bins.B_PAGE)
4095 searchStart += bPageOffsets[searchStart]; 4094 searchStart += bPageOffsets[searchStart];
4096 4095
4097 for (size_t i = searchStart; i < npages; ) 4096 for (size_t i = searchStart; i < npages; )
4098 { 4097 {
4099 assert(pagetable[i] == B_FREE); 4098 assert(pagetable[i] == Bins.B_FREE);
4100 4099
4101 auto p = bPageOffsets[i]; 4100 auto p = bPageOffsets[i];
4102 if (p > n) 4101 if (p > n)
@@ -4107,11 +4106,11 @@ struct LargeObjectPool
4107 if (p == n) 4106 if (p == n)
4108 { 4107 {
4109 L_found: 4108 L_found:
4110 pagetable[i] = B_PAGE; 4109 pagetable[i] = Bins.B_PAGE;
4111 bPageOffsets[i] = cast(uint) n; 4110 bPageOffsets[i] = cast(uint) n;
4112 if (n > 1) 4111 if (n > 1)
4113 { 4112 {
4114 memset(&pagetable[i + 1], B_PAGEPLUS, n - 1); 4113 memset(&pagetable[i + 1], Bins.B_PAGEPLUS, n - 1);
4115 for (auto offset = 1; offset < n; offset++) 4114 for (auto offset = 1; offset < n; offset++)
4116 bPageOffsets[i + offset] = cast(uint) offset; 4115 bPageOffsets[i + offset] = cast(uint) offset;
4117 } 4116 }
@@ -4122,7 +4121,7 @@ struct LargeObjectPool
4122 largest = p; 4121 largest = p;
4123 4122
4124 i += p; 4123 i += p;
4125 while (i < npages && pagetable[i] == B_PAGE) 4124 while (i < npages && pagetable[i] == Bins.B_PAGE)
4126 { 4125 {
4127 // we have the size information, so we skip a whole bunch of pages. 4126 // we have the size information, so we skip a whole bunch of pages.
4128 i += bPageOffsets[i]; 4127 i += bPageOffsets[i];
@@ -4145,8 +4144,8 @@ struct LargeObjectPool
4145 4144
4146 for (size_t i = pagenum; i < npages + pagenum; i++) 4145 for (size_t i = pagenum; i < npages + pagenum; i++)
4147 { 4146 {
4148 assert(pagetable[i] < B_FREE); 4147 assert(pagetable[i] < Bins.B_FREE);
4149 pagetable[i] = B_FREE; 4148 pagetable[i] = Bins.B_FREE;
4150 } 4149 }
4151 freepages += npages; 4150 freepages += npages;
4152 largestFree = freepages; // invalidate 4151 largestFree = freepages; // invalidate
@@ -4157,8 +4156,8 @@ struct LargeObjectPool
4157 */ 4156 */
4158 void setFreePageOffsets(size_t page, size_t num) nothrow @nogc 4157 void setFreePageOffsets(size_t page, size_t num) nothrow @nogc
4159 { 4158 {
4160 assert(pagetable[page] == B_FREE); 4159 assert(pagetable[page] == Bins.B_FREE);
4161 assert(pagetable[page + num - 1] == B_FREE); 4160 assert(pagetable[page + num - 1] == Bins.B_FREE);
4162 bPageOffsets[page] = cast(uint)num; 4161 bPageOffsets[page] = cast(uint)num;
4163 if (num > 1) 4162 if (num > 1)
4164 bPageOffsets[page + num - 1] = cast(uint)num; 4163 bPageOffsets[page + num - 1] = cast(uint)num;
@@ -4168,7 +4167,7 @@ struct LargeObjectPool
4168 { 4167 {
4169 static if (bwd) 4168 static if (bwd)
4170 { 4169 {
4171 if (page > 0 && pagetable[page - 1] == B_FREE) 4170 if (page > 0 && pagetable[page - 1] == Bins.B_FREE)
4172 { 4171 {
4173 auto sz = bPageOffsets[page - 1]; 4172 auto sz = bPageOffsets[page - 1];
4174 page -= sz; 4173 page -= sz;
@@ -4177,7 +4176,7 @@ struct LargeObjectPool
4177 } 4176 }
4178 static if (fwd) 4177 static if (fwd)
4179 { 4178 {
4180 if (page + num < npages && pagetable[page + num] == B_FREE) 4179 if (page + num < npages && pagetable[page + num] == Bins.B_FREE)
4181 num += bPageOffsets[page + num]; 4180 num += bPageOffsets[page + num];
4182 } 4181 }
4183 setFreePageOffsets(page, num); 4182 setFreePageOffsets(page, num);
@@ -4197,8 +4196,8 @@ struct LargeObjectPool
4197 if (cast(size_t)p & (PAGESIZE - 1)) // check for interior pointer 4196 if (cast(size_t)p & (PAGESIZE - 1)) // check for interior pointer
4198 return 0; 4197 return 0;
4199 size_t pagenum = pagenumOf(p); 4198 size_t pagenum = pagenumOf(p);
4200 Bins bin = cast(Bins)pagetable[pagenum]; 4199 Bins bin = pagetable[pagenum];
4201 if (bin != B_PAGE) 4200 if (bin != Bins.B_PAGE)
4202 return 0; 4201 return 0;
4203 return bPageOffsets[pagenum]; 4202 return bPageOffsets[pagenum];
4204 } 4203 }
@@ -4208,7 +4207,7 @@ struct LargeObjectPool
4208 */ 4207 */
4209 size_t getSize(size_t pn) const nothrow @nogc 4208 size_t getSize(size_t pn) const nothrow @nogc
4210 { 4209 {
4211 assert(pagetable[pn] == B_PAGE); 4210 assert(pagetable[pn] == Bins.B_PAGE);
4212 return cast(size_t) bPageOffsets[pn] * PAGESIZE; 4211 return cast(size_t) bPageOffsets[pn] * PAGESIZE;
4213 } 4212 }
4214 4213
@@ -4221,11 +4220,11 @@ struct LargeObjectPool
4221 4220
4222 size_t offset = cast(size_t)(p - baseAddr); 4221 size_t offset = cast(size_t)(p - baseAddr);
4223 size_t pn = offset / PAGESIZE; 4222 size_t pn = offset / PAGESIZE;
4224 Bins bin = cast(Bins)pagetable[pn]; 4223 Bins bin = pagetable[pn];
4225 4224
4226 if (bin == B_PAGEPLUS) 4225 if (bin == Bins.B_PAGEPLUS)
4227 pn -= bPageOffsets[pn]; 4226 pn -= bPageOffsets[pn];
4228 else if (bin != B_PAGE) 4227 else if (bin != Bins.B_PAGE)
4229 return info; // no info for free pages 4228 return info; // no info for free pages
4230 4229
4231 info.base = baseAddr + pn * PAGESIZE; 4230 info.base = baseAddr + pn * PAGESIZE;
@@ -4238,8 +4237,8 @@ struct LargeObjectPool
4238 { 4237 {
4239 foreach (pn; 0 .. npages) 4238 foreach (pn; 0 .. npages)
4240 { 4239 {
4241 Bins bin = cast(Bins)pagetable[pn]; 4240 Bins bin = pagetable[pn];
4242 if (bin > B_PAGE) 4241 if (bin > Bins.B_PAGE)
4243 continue; 4242 continue;
4244 size_t biti = pn; 4243 size_t biti = pn;
4245 4244
@@ -4265,7 +4264,7 @@ struct LargeObjectPool
4265 4264
4266 size_t n = 1; 4265 size_t n = 1;
4267 for (; pn + n < npages; ++n) 4266 for (; pn + n < npages; ++n)
4268 if (pagetable[pn + n] != B_PAGEPLUS) 4267 if (pagetable[pn + n] != Bins.B_PAGEPLUS)
4269 break; 4268 break;
4270 debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE); 4269 debug (MEMSTOMP) memset(baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE);
4271 freePages(pn, n); 4270 freePages(pn, n);
@@ -4285,7 +4284,7 @@ struct SmallObjectPool
4285 { 4284 {
4286 //base.Invariant(); 4285 //base.Invariant();
4287 uint cntRecover = 0; 4286 uint cntRecover = 0;
4288 foreach (Bins bin; 0 .. B_NUMSMALL) 4287 foreach (Bins bin; Bins.B_16 .. Bins.B_NUMSMALL)
4289 { 4288 {
4290 for (auto pn = recoverPageFirst[bin]; pn < npages; pn = binPageChain[pn]) 4289 for (auto pn = recoverPageFirst[bin]; pn < npages; pn = binPageChain[pn])
4291 { 4290 {
@@ -4296,7 +4295,7 @@ struct SmallObjectPool
4296 uint cntFree = 0; 4295 uint cntFree = 0;
4297 for (auto pn = searchStart; pn < npages; pn = binPageChain[pn]) 4296 for (auto pn = searchStart; pn < npages; pn = binPageChain[pn])
4298 { 4297 {
4299 assert(pagetable[pn] == B_FREE); 4298 assert(pagetable[pn] == Bins.B_FREE);
4300 cntFree++; 4299 cntFree++;
4301 } 4300 }
4302 assert(cntFree == freepages); 4301 assert(cntFree == freepages);
@@ -4315,8 +4314,8 @@ struct SmallObjectPool
4315 do 4314 do
4316 { 4315 {
4317 size_t pagenum = pagenumOf(p); 4316 size_t pagenum = pagenumOf(p);
4318 Bins bin = cast(Bins)pagetable[pagenum]; 4317 Bins bin = pagetable[pagenum];
4319 assert(bin < B_PAGE); 4318 assert(bin < Bins.B_PAGE);
4320 if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer 4319 if (p != cast(void*)baseOffset(cast(size_t)p, bin)) // check for interior pointer
4321 return 0; 4320 return 0;
4322 const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small; 4321 const biti = cast(size_t)(p - baseAddr) >> ShiftBy.Small;
@@ -4330,9 +4329,9 @@ struct SmallObjectPool
4330 BlkInfo info; 4329 BlkInfo info;
4331 size_t offset = cast(size_t)(p - baseAddr); 4330 size_t offset = cast(size_t)(p - baseAddr);
4332 size_t pn = offset / PAGESIZE; 4331 size_t pn = offset / PAGESIZE;
4333 Bins bin = cast(Bins)pagetable[pn]; 4332 Bins bin = pagetable[pn];
4334 4333
4335 if (bin >= B_PAGE) 4334 if (bin >= Bins.B_PAGE)
4336 return info; 4335 return info;
4337 4336
4338 auto base = cast(void*)baseOffset(cast(size_t)p, bin); 4337 auto base = cast(void*)baseOffset(cast(size_t)p, bin);
@@ -4352,8 +4351,8 @@ struct SmallObjectPool
4352 { 4351 {
4353 foreach (pn; 0 .. npages) 4352 foreach (pn; 0 .. npages)
4354 { 4353 {
4355 Bins bin = cast(Bins)pagetable[pn]; 4354 Bins bin = pagetable[pn];
4356 if (bin >= B_PAGE) 4355 if (bin >= Bins.B_PAGE)
4357 continue; 4356 continue;
4358 4357
4359 immutable size = binsize[bin]; 4358 immutable size = binsize[bin];
@@ -4404,13 +4403,13 @@ struct SmallObjectPool
4404 if (searchStart >= npages) 4403 if (searchStart >= npages)
4405 return null; 4404 return null;
4406 4405
4407 assert(pagetable[searchStart] == B_FREE); 4406 assert(pagetable[searchStart] == Bins.B_FREE);
4408 4407
4409 L1: 4408 L1:
4410 size_t pn = searchStart; 4409 size_t pn = searchStart;
4411 searchStart = binPageChain[searchStart]; 4410 searchStart = binPageChain[searchStart];
4412 binPageChain[pn] = Pool.PageRecovered; 4411 binPageChain[pn] = Pool.PageRecovered;
4413 pagetable[pn] = cast(ubyte)bin; 4412 pagetable[pn] = bin;
4414 freepages--; 4413 freepages--;
4415 4414
4416 // Convert page to free list 4415 // Convert page to free list
@@ -4537,7 +4536,7 @@ debug(PRINTF) void printFreeInfo(Pool* pool) nothrow
4537{ 4536{
4538 uint nReallyFree; 4537 uint nReallyFree;
4539 foreach (i; 0..pool.npages) { 4538 foreach (i; 0..pool.npages) {
4540 if (pool.pagetable[i] >= B_FREE) nReallyFree++; 4539 if (pool.pagetable[i] >= Bins.B_FREE) nReallyFree++;
4541 } 4540 }
4542 4541
4543 printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages); 4542 printf("Pool %p: %d really free, %d supposedly free\n", pool, nReallyFree, pool.freepages);
@@ -4770,7 +4769,7 @@ debug (LOGGING)
4770 size_t offset = cast(size_t)(p - pool.baseAddr); 4769 size_t offset = cast(size_t)(p - pool.baseAddr);
4771 size_t biti; 4770 size_t biti;
4772 size_t pn = offset / PAGESIZE; 4771 size_t pn = offset / PAGESIZE;
4773 Bins bin = cast(Bins)pool.pagetable[pn]; 4772 Bins bin = pool.pagetable[pn];
4774 biti = (offset & (PAGESIZE - 1)) >> pool.shiftBy; 4773 biti = (offset & (PAGESIZE - 1)) >> pool.shiftBy;
4775 debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti); 4774 debug(PRINTF) printf("\tbin = %d, offset = x%x, biti = x%x\n", bin, offset, biti);
4776 } 4775 }
@@ -4921,7 +4920,7 @@ unittest
4921 assert(p + (260 << 20) == q); 4920 assert(p + (260 << 20) == q);
4922 assert(q + (65 << 20) == r); 4921 assert(q + (65 << 20) == r);
4923 GC.free(q); 4922 GC.free(q);
4924 // should trigger "assert(bin == B_FREE);" in mark due to dangling pointer q: 4923 // should trigger "assert(bin == Bins.B_FREE);" in mark due to dangling pointer q:
4925 GC.collect(); 4924 GC.collect();
4926 // should trigger "break;" in extendNoSync: 4925 // should trigger "break;" in extendNoSync:
4927 size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited) 4926 size_t sz = GC.extend(p, 64 << 20, 66 << 20); // trigger size after p large enough (but limited)
diff --git a/libphobos/libdruntime/core/internal/gc/pooltable.d b/libphobos/libdruntime/core/internal/gc/pooltable.d
index 5924f9c1a55..096633825a2 100644
--- a/libphobos/libdruntime/core/internal/gc/pooltable.d
+++ b/libphobos/libdruntime/core/internal/gc/pooltable.d
@@ -13,15 +13,14 @@ struct PoolTable(Pool)
13{ 13{
14 import core.stdc.string : memmove; 14 import core.stdc.string : memmove;
15 15
16nothrow: 16 void Dtor() nothrow @nogc
17 void Dtor()
18 { 17 {
19 cstdlib.free(pools); 18 cstdlib.free(pools);
20 pools = null; 19 pools = null;
21 npools = 0; 20 npools = 0;
22 } 21 }
23 22
24 bool insert(Pool* pool) 23 bool insert(Pool* pool) nothrow @nogc
25 { 24 {
26 auto newpools = cast(Pool **)cstdlib.realloc(pools, (npools + 1) * pools[0].sizeof); 25 auto newpools = cast(Pool **)cstdlib.realloc(pools, (npools + 1) * pools[0].sizeof);
27 if (!newpools) 26 if (!newpools)
@@ -51,25 +50,31 @@ nothrow:
51 return true; 50 return true;
52 } 51 }
53 52
54 @property size_t length() pure const 53 @property size_t length() const scope @safe pure nothrow @nogc
55 { 54 {
56 return npools; 55 return npools;
57 } 56 }
58 57
59 ref inout(Pool*) opIndex(size_t idx) inout pure 58 ref inout(Pool*) opIndex(size_t idx) inout return @trusted pure nothrow @nogc
60 in { assert(idx < length); } 59 in { assert(idx < length); }
61 do 60 do
62 { 61 {
63 return pools[idx]; 62 return pools[idx];
64 } 63 }
65 64
66 inout(Pool*)[] opSlice(size_t a, size_t b) inout pure 65 inout(Pool*)[] opSlice(size_t a, size_t b) inout return @trusted pure nothrow @nogc
67 in { assert(a <= length && b <= length); } 66 in { assert(a <= length && b <= length); }
68 do 67 do
69 { 68 {
70 return pools[a .. b]; 69 return pools[a .. b];
71 } 70 }
72 71
72 /// Returns: A slice over all pools in this `PoolTable`
73 inout(Pool*)[] opSlice() inout return @trusted pure nothrow @nogc
74 {
75 return this.pools[0 .. this.length];
76 }
77
73 alias opDollar = length; 78 alias opDollar = length;
74 79
75 /** 80 /**
@@ -77,7 +82,7 @@ nothrow:
77 * Return null if not in a Pool. 82 * Return null if not in a Pool.
78 * Assume pooltable[] is sorted. 83 * Assume pooltable[] is sorted.
79 */ 84 */
80 Pool *findPool(void *p) nothrow 85 Pool *findPool(void *p) nothrow @nogc
81 { 86 {
82 if (p >= minAddr && p < maxAddr) 87 if (p >= minAddr && p < maxAddr)
83 { 88 {
@@ -109,7 +114,7 @@ nothrow:
109 } 114 }
110 115
111 // semi-stable partition, returns right half for which pred is false 116 // semi-stable partition, returns right half for which pred is false
112 Pool*[] minimize() pure 117 Pool*[] minimize() pure nothrow @nogc
113 { 118 {
114 static void swap(ref Pool* a, ref Pool* b) 119 static void swap(ref Pool* a, ref Pool* b)
115 { 120 {
@@ -151,7 +156,7 @@ nothrow:
151 return pools[npools .. len]; 156 return pools[npools .. len];
152 } 157 }
153 158
154 void Invariant() const 159 void Invariant() const nothrow @nogc
155 { 160 {
156 if (!npools) return; 161 if (!npools) return;
157 162
@@ -165,8 +170,8 @@ nothrow:
165 assert(_maxAddr == pools[npools - 1].topAddr); 170 assert(_maxAddr == pools[npools - 1].topAddr);
166 } 171 }
167 172
168 @property const(void)* minAddr() pure const { return _minAddr; } 173 @property const(void)* minAddr() const @safe pure nothrow @nogc { return _minAddr; }
169 @property const(void)* maxAddr() pure const { return _maxAddr; } 174 @property const(void)* maxAddr() const @safe pure nothrow @nogc { return _maxAddr; }
170 175
171package: 176package:
172 Pool** pools; 177 Pool** pools;
@@ -184,7 +189,7 @@ unittest
184 { 189 {
185 byte* baseAddr, topAddr; 190 byte* baseAddr, topAddr;
186 size_t freepages, npages, ptIndex; 191 size_t freepages, npages, ptIndex;
187 @property bool isFree() const pure nothrow { return freepages == npages; } 192 @property bool isFree() const scope pure nothrow @nogc { return freepages == npages; }
188 } 193 }
189 PoolTable!MockPool pooltable; 194 PoolTable!MockPool pooltable;
190 195
diff --git a/libphobos/libdruntime/core/internal/gc/proxy.d b/libphobos/libdruntime/core/internal/gc/proxy.d
index 2c89472a558..695ef061a81 100644
--- a/libphobos/libdruntime/core/internal/gc/proxy.d
+++ b/libphobos/libdruntime/core/internal/gc/proxy.d
@@ -209,12 +209,12 @@ extern (C)
209 return instance.query( p ); 209 return instance.query( p );
210 } 210 }
211 211
212 core.memory.GC.Stats gc_stats() nothrow 212 core.memory.GC.Stats gc_stats() @safe nothrow @nogc
213 { 213 {
214 return instance.stats(); 214 return instance.stats();
215 } 215 }
216 216
217 core.memory.GC.ProfileStats gc_profileStats() nothrow @safe 217 core.memory.GC.ProfileStats gc_profileStats() @safe nothrow @nogc
218 { 218 {
219 return instance.profileStats(); 219 return instance.profileStats();
220 } 220 }
diff --git a/libphobos/libdruntime/core/memory.d b/libphobos/libdruntime/core/memory.d
index 6ba569a241c..f25ba6f1d46 100644
--- a/libphobos/libdruntime/core/memory.d
+++ b/libphobos/libdruntime/core/memory.d
@@ -133,7 +133,7 @@ private
133 } 133 }
134 134
135 extern (C) BlkInfo_ gc_query(return scope void* p) pure nothrow; 135 extern (C) BlkInfo_ gc_query(return scope void* p) pure nothrow;
136 extern (C) GC.Stats gc_stats ( ) nothrow @nogc; 136 extern (C) GC.Stats gc_stats ( ) @safe nothrow @nogc;
137 extern (C) GC.ProfileStats gc_profileStats ( ) nothrow @nogc @safe; 137 extern (C) GC.ProfileStats gc_profileStats ( ) nothrow @nogc @safe;
138} 138}
139 139
@@ -766,7 +766,7 @@ extern(D):
766 * Returns runtime stats for currently active GC implementation 766 * Returns runtime stats for currently active GC implementation
767 * See `core.memory.GC.Stats` for list of available metrics. 767 * See `core.memory.GC.Stats` for list of available metrics.
768 */ 768 */
769 static Stats stats() nothrow 769 static Stats stats() @safe nothrow @nogc
770 { 770 {
771 return gc_stats(); 771 return gc_stats();
772 } 772 }
diff --git a/libphobos/libdruntime/core/stdcpp/string.d b/libphobos/libdruntime/core/stdcpp/string.d
index 1cdb0f40952..dfec1ec9f7e 100644
--- a/libphobos/libdruntime/core/stdcpp/string.d
+++ b/libphobos/libdruntime/core/stdcpp/string.d
@@ -343,7 +343,7 @@ extern(D):
343 /// 343 ///
344 inout(T)* data() inout @safe { return _Get_data()._Myptr; } 344 inout(T)* data() inout @safe { return _Get_data()._Myptr; }
345 /// 345 ///
346 inout(T)[] as_array() inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; } 346 inout(T)[] as_array() return scope inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize]; }
347 /// 347 ///
348 ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; } 348 ref inout(T) at(size_type i) inout nothrow @trusted { return _Get_data()._Myptr[0 .. _Get_data()._Mysize][i]; }
349 349
@@ -1920,7 +1920,7 @@ extern(D):
1920 /// 1920 ///
1921 inout(T)* data() inout @safe { return __get_pointer(); } 1921 inout(T)* data() inout @safe { return __get_pointer(); }
1922 /// 1922 ///
1923 inout(T)[] as_array() inout nothrow @trusted { return __get_pointer()[0 .. size()]; } 1923 inout(T)[] as_array() return scope inout nothrow @trusted { return __get_pointer()[0 .. size()]; }
1924 /// 1924 ///
1925 ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; } 1925 ref inout(T) at(size_type i) inout nothrow @trusted { return __get_pointer()[0 .. size()][i]; }
1926 1926
@@ -2497,8 +2497,8 @@ extern(C++, (StdNamespace)):
2497 extern(D) @safe @nogc: 2497 extern(D) @safe @nogc:
2498 pragma(inline, true) 2498 pragma(inline, true)
2499 { 2499 {
2500 ref inout(Alloc) _Getal() inout pure nothrow { return _Mypair._Myval1; } 2500 ref inout(Alloc) _Getal() return inout pure nothrow { return _Mypair._Myval1; }
2501 ref inout(ValTy) _Get_data() inout pure nothrow { return _Mypair._Myval2; } 2501 ref inout(ValTy) _Get_data() return inout pure nothrow { return _Mypair._Myval2; }
2502 } 2502 }
2503 2503
2504 void _Orphan_all() nothrow { _Get_data._Base._Orphan_all(); } 2504 void _Orphan_all() nothrow { _Get_data._Base._Orphan_all(); }
diff --git a/libphobos/libdruntime/core/sys/posix/sys/stat.d b/libphobos/libdruntime/core/sys/posix/sys/stat.d
index 22f4df66455..1fb4e44cbbf 100644
--- a/libphobos/libdruntime/core/sys/posix/sys/stat.d
+++ b/libphobos/libdruntime/core/sys/posix/sys/stat.d
@@ -388,50 +388,93 @@ version (linux)
388 { 388 {
389 struct stat_t 389 struct stat_t
390 { 390 {
391 c_ulong st_dev; 391 dev_t st_dev;
392 ino_t st_ino; 392 static if (!__USE_FILE_OFFSET64)
393 {
394 ushort __pad1;
395 ino_t st_ino;
396 }
397 else
398 ino_t st_ino;
393 mode_t st_mode; 399 mode_t st_mode;
394 nlink_t st_nlink; 400 nlink_t st_nlink;
395 uid_t st_uid; 401 uid_t st_uid;
396 gid_t st_gid; 402 gid_t st_gid;
397 c_ulong st_rdev; 403 dev_t st_rdev;
404 ushort __pad2;
398 off_t st_size; 405 off_t st_size;
399 c_ulong st_blksize; 406 blksize_t st_blksize;
400 c_ulong st_blocks; 407 blkcnt_t st_blocks;
401 c_ulong st_atime; 408 static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
402 c_ulong st_atime_nsec; 409 {
403 c_ulong st_mtime; 410 timespec st_atim;
404 c_ulong st_mtime_nsec; 411 timespec st_mtim;
405 c_ulong st_ctime; 412 timespec st_ctim;
406 c_ulong st_ctime_nsec; 413 extern(D) @safe @property inout pure nothrow
414 {
415 ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
416 ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
417 ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
418 }
419 }
420 else
421 {
422 time_t st_atime;
423 c_ulong st_atimensec;
424 time_t st_mtime;
425 c_ulong st_mtimensec;
426 time_t st_ctime;
427 c_ulong st_ctimensec;
428 }
407 c_ulong __unused4; 429 c_ulong __unused4;
408 c_ulong __unused5; 430 c_ulong __unused5;
409 } 431 }
432 static if (__USE_FILE_OFFSET64)
433 static assert(stat_t.sizeof == 104);
434 else
435 static assert(stat_t.sizeof == 88);
410 } 436 }
411 else version (PPC64) 437 else version (PPC64)
412 { 438 {
413 struct stat_t 439 struct stat_t
414 { 440 {
415 c_ulong st_dev; 441 dev_t st_dev;
416 ino_t st_ino; 442 ino_t st_ino;
417 nlink_t st_nlink; 443 nlink_t st_nlink;
418 mode_t st_mode; 444 mode_t st_mode;
419 uid_t st_uid; 445 uid_t st_uid;
420 gid_t st_gid; 446 gid_t st_gid;
421 c_ulong st_rdev; 447 int __pad2;
448 dev_t st_rdev;
422 off_t st_size; 449 off_t st_size;
423 c_ulong st_blksize; 450 blksize_t st_blksize;
424 c_ulong st_blocks; 451 blkcnt_t st_blocks;
425 c_ulong st_atime; 452 static if (_DEFAULT_SOURCE || _XOPEN_SOURCE >= 700)
426 c_ulong st_atime_nsec; 453 {
427 c_ulong st_mtime; 454 timespec st_atim;
428 c_ulong st_mtime_nsec; 455 timespec st_mtim;
429 c_ulong st_ctime; 456 timespec st_ctim;
430 c_ulong st_ctime_nsec; 457 extern(D) @safe @property inout pure nothrow
458 {
459 ref inout(time_t) st_atime() return { return st_atim.tv_sec; }
460 ref inout(time_t) st_mtime() return { return st_mtim.tv_sec; }
461 ref inout(time_t) st_ctime() return { return st_ctim.tv_sec; }
462 }
463 }
464 else
465 {
466 time_t st_atime;
467 c_ulong st_atimensec;
468 time_t st_mtime;
469 c_ulong st_mtimensec;
470 time_t st_ctime;
471 c_ulong st_ctimensec;
472 }
431 c_ulong __unused4; 473 c_ulong __unused4;
432 c_ulong __unused5; 474 c_ulong __unused5;
433 c_ulong __unused6; 475 c_ulong __unused6;
434 } 476 }
477 static assert(stat_t.sizeof == 144);
435 } 478 }
436 else version (RISCV_Any) 479 else version (RISCV_Any)
437 { 480 {
diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d
index 0ddf62f478c..91f218e27fb 100644
--- a/libphobos/libdruntime/core/time.d
+++ b/libphobos/libdruntime/core/time.d
@@ -496,6 +496,81 @@ assert(std.datetime.Date(2010, 9, 7) - std.datetime.Date(2010, 10, 3) ==
496 +/ 496 +/
497struct Duration 497struct Duration
498{ 498{
499 /++
500 Converts this `Duration` to a `string`.
501
502 The string is meant to be human readable, not machine parseable (e.g.
503 whether there is an `'s'` on the end of the unit name usually depends on
504 whether it's plural or not, and empty units are not included unless the
505 Duration is `zero`). Any code needing a specific string format should
506 use `total` or `split` to get the units needed to create the desired
507 string format and create the string itself.
508
509 The format returned by toString may or may not change in the future.
510
511 Params:
512 sink = A sink object, expected to be a delegate or aggregate
513 implementing `opCall` that accepts a `scope const(char)[]`
514 as argument.
515 +/
516 void toString (SinkT) (scope SinkT sink) const scope
517 {
518 static immutable units = [
519 "weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs"
520 ];
521
522 static void appListSep(SinkT sink, uint pos, bool last)
523 {
524 if (pos == 0)
525 return;
526 if (!last)
527 sink(", ");
528 else
529 sink(pos == 1 ? " and " : ", and ");
530 }
531
532 static void appUnitVal(string units)(SinkT sink, long val)
533 {
534 immutable plural = val != 1;
535 string unit;
536 static if (units == "seconds")
537 unit = plural ? "secs" : "sec";
538 else static if (units == "msecs")
539 unit = "ms";
540 else static if (units == "usecs")
541 unit = "μs";
542 else
543 unit = plural ? units : units[0 .. $-1];
544 sink(signedToTempString(val));
545 sink(" ");
546 sink(unit);
547 }
548
549 if (_hnsecs == 0)
550 {
551 sink("0 hnsecs");
552 return;
553 }
554
555 long hnsecs = _hnsecs;
556 uint pos;
557 static foreach (unit; units)
558 {
559 if (auto val = splitUnitsFromHNSecs!unit(hnsecs))
560 {
561 appListSep(sink, pos++, hnsecs == 0);
562 appUnitVal!unit(sink, val);
563 }
564 if (hnsecs == 0)
565 return;
566 }
567 if (hnsecs != 0)
568 {
569 appListSep(sink, pos++, true);
570 appUnitVal!"hnsecs"(sink, hnsecs);
571 }
572 }
573
499@safe pure: 574@safe pure:
500 575
501public: 576public:
@@ -1539,71 +1614,12 @@ public:
1539 } 1614 }
1540 } 1615 }
1541 1616
1542 1617 /// Ditto
1543 /++ 1618 string toString() const scope nothrow
1544 Converts this `Duration` to a `string`.
1545
1546 The string is meant to be human readable, not machine parseable (e.g.
1547 whether there is an `'s'` on the end of the unit name usually depends on
1548 whether it's plural or not, and empty units are not included unless the
1549 Duration is `zero`). Any code needing a specific string format should
1550 use `total` or `split` to get the units needed to create the desired
1551 string format and create the string itself.
1552
1553 The format returned by toString may or may not change in the future.
1554 +/
1555 string toString() const nothrow pure @safe
1556 { 1619 {
1557 static void appListSep(ref string res, uint pos, bool last) 1620 string result;
1558 { 1621 this.toString((in char[] data) { result ~= data; });
1559 if (pos == 0) 1622 return result;
1560 return;
1561 if (!last)
1562 res ~= ", ";
1563 else
1564 res ~= pos == 1 ? " and " : ", and ";
1565 }
1566
1567 static void appUnitVal(string units)(ref string res, long val)
1568 {
1569 immutable plural = val != 1;
1570 string unit;
1571 static if (units == "seconds")
1572 unit = plural ? "secs" : "sec";
1573 else static if (units == "msecs")
1574 unit = "ms";
1575 else static if (units == "usecs")
1576 unit = "μs";
1577 else
1578 unit = plural ? units : units[0 .. $-1];
1579 res ~= signedToTempString(val);
1580 res ~= " ";
1581 res ~= unit;
1582 }
1583
1584 if (_hnsecs == 0)
1585 return "0 hnsecs";
1586
1587 template TT(T...) { alias T TT; }
1588 alias units = TT!("weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs");
1589
1590 long hnsecs = _hnsecs; string res; uint pos;
1591 foreach (unit; units)
1592 {
1593 if (auto val = splitUnitsFromHNSecs!unit(hnsecs))
1594 {
1595 appListSep(res, pos++, hnsecs == 0);
1596 appUnitVal!unit(res, val);
1597 }
1598 if (hnsecs == 0)
1599 break;
1600 }
1601 if (hnsecs != 0)
1602 {
1603 appListSep(res, pos++, true);
1604 appUnitVal!"hnsecs"(res, hnsecs);
1605 }
1606 return res;
1607 } 1623 }
1608 1624
1609 /// 1625 ///
@@ -1731,6 +1747,20 @@ unittest
1731 assert(myTime == 123.msecs); 1747 assert(myTime == 123.msecs);
1732} 1748}
1733 1749
1750// Ensure `toString` doesn't allocate if the sink doesn't
1751version (CoreUnittest) @safe pure nothrow @nogc unittest
1752{
1753 char[256] buffer; size_t len;
1754 scope sink = (in char[] data) {
1755 assert(data.length + len <= buffer.length);
1756 buffer[len .. len + data.length] = data[];
1757 len += data.length;
1758 };
1759 auto dur = Duration(-12_096_020_900_003);
1760 dur.toString(sink);
1761 assert(buffer[0 .. len] == "-2 weeks, -2 secs, -90 ms, and -3 hnsecs");
1762}
1763
1734/++ 1764/++
1735 Converts a $(D TickDuration) to the given units as either an integral 1765 Converts a $(D TickDuration) to the given units as either an integral
1736 value or a floating point value. 1766 value or a floating point value.
diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d
index cf7cf967aa6..56a2efe735a 100644
--- a/libphobos/libdruntime/object.d
+++ b/libphobos/libdruntime/object.d
@@ -2649,13 +2649,18 @@ class Throwable : Object
2649 2649
2650 /** 2650 /**
2651 * Get the message describing the error. 2651 * Get the message describing the error.
2652 * Base behavior is to return the `Throwable.msg` field. 2652 *
2653 * Override to return some other error message. 2653 * This getter is an alternative way to access the Exception's message,
2654 * with the added advantage of being override-able in subclasses.
2655 * Subclasses are hence free to do their own memory managements without
2656 * being tied to the requirement of providing a `string` in a field.
2657 *
2658 * The default behavior is to return the `Throwable.msg` field.
2654 * 2659 *
2655 * Returns: 2660 * Returns:
2656 * Error message 2661 * A message representing the cause of the `Throwable`
2657 */ 2662 */
2658 @__future const(char)[] message() const 2663 @__future const(char)[] message() const @safe nothrow
2659 { 2664 {
2660 return this.msg; 2665 return this.msg;
2661 } 2666 }
diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE
index b5b939f41b3..e15541e181d 100644
--- a/libphobos/src/MERGE
+++ b/libphobos/src/MERGE
@@ -1,4 +1,4 @@
11a3e80ec25afab6123cdcfe20186f36f006b68bb 141aaf8c2636df0e2e3ad39933b321d2b4cd231fa
2 2
3The first line of this file holds the git revision number of the last 3The first line of this file holds the git revision number of the last
4merge done from the dlang/phobos repository. 4merge done from the dlang/phobos repository.
diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d
index a99c5170afb..b09b82ab85e 100644
--- a/libphobos/src/std/file.d
+++ b/libphobos/src/std/file.d
@@ -425,10 +425,10 @@ version (Windows) private void[] readImpl(scope const(char)[] name, scope const(
425 fileSize = makeUlong(sizeLow, sizeHigh); 425 fileSize = makeUlong(sizeLow, sizeHigh);
426 return result; 426 return result;
427 } 427 }
428 static trustedReadFile(HANDLE hFile, void *lpBuffer, ulong nNumberOfBytesToRead) 428 static trustedReadFile(HANDLE hFile, void *lpBuffer, size_t nNumberOfBytesToRead)
429 { 429 {
430 // Read by chunks of size < 4GB (Windows API limit) 430 // Read by chunks of size < 4GB (Windows API limit)
431 ulong totalNumRead = 0; 431 size_t totalNumRead = 0;
432 while (totalNumRead != nNumberOfBytesToRead) 432 while (totalNumRead != nNumberOfBytesToRead)
433 { 433 {
434 const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000); 434 const uint chunkSize = min(nNumberOfBytesToRead - totalNumRead, 0xffff_0000);
diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d
index 482aae6306a..c1c5cd2b36e 100644
--- a/libphobos/src/std/getopt.d
+++ b/libphobos/src/std/getopt.d
@@ -438,7 +438,7 @@ GetoptResult getopt(T...)(ref string[] args, T opts)
438} 438}
439 439
440/// 440///
441@system unittest 441@safe unittest
442{ 442{
443 auto args = ["prog", "--foo", "-b"]; 443 auto args = ["prog", "--foo", "-b"];
444 444
@@ -1646,11 +1646,13 @@ Params:
1646 text = The text to printed at the beginning of the help output. 1646 text = The text to printed at the beginning of the help output.
1647 opt = The `Option` extracted from the `getopt` parameter. 1647 opt = The `Option` extracted from the `getopt` parameter.
1648*/ 1648*/
1649void defaultGetoptPrinter(string text, Option[] opt) 1649void defaultGetoptPrinter(string text, Option[] opt) @safe
1650{ 1650{
1651 import std.stdio : stdout; 1651 import std.stdio : stdout;
1652 // stdout global __gshared is trusted with a locked text writer
1653 auto w = (() @trusted => stdout.lockingTextWriter())();
1652 1654
1653 defaultGetoptFormatter(stdout.lockingTextWriter(), text, opt); 1655 defaultGetoptFormatter(w, text, opt);
1654} 1656}
1655 1657
1656/** This function writes the passed text and `Option` into an output range 1658/** This function writes the passed text and `Option` into an output range
diff --git a/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d
index c092a9d0946..31f58fa5fa9 100644
--- a/libphobos/src/std/range/primitives.d
+++ b/libphobos/src/std/range/primitives.d
@@ -2055,9 +2055,14 @@ if (isBidirectionalRange!Range)
2055} 2055}
2056 2056
2057/** 2057/**
2058 Moves the front of `r` out and returns it. Leaves `r.front` in a 2058 Moves the front of `r` out and returns it.
2059 destroyable state that does not allocate any resources (usually equal 2059
2060 to its `.init` value). 2060 If `r.front` is a struct with a destructor or copy constructor defined, it
2061 is reset to its `.init` value after its value is moved. Otherwise, it is
2062 left unchanged.
2063
2064 In either case, `r.front` is left in a destroyable state that does not
2065 allocate any resources.
2061*/ 2066*/
2062ElementType!R moveFront(R)(R r) 2067ElementType!R moveFront(R)(R r)
2063{ 2068{
diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d
index 5e35a6b9cd5..0dd636ea67b 100644
--- a/libphobos/src/std/sumtype.d
+++ b/libphobos/src/std/sumtype.d
@@ -533,15 +533,35 @@ public:
533 /** 533 /**
534 * Assigns a value to a `SumType`. 534 * Assigns a value to a `SumType`.
535 * 535 *
536 * Assigning to a `SumType` is `@system` if any of the 536 * If any of the `SumType`'s members other than the one being assigned
537 * `SumType`'s members contain pointers or references, since 537 * to contain pointers or references, it is possible for the assignment
538 * those members may be reachable through external references, 538 * to cause memory corruption (see the
539 * and overwriting them could therefore lead to memory 539 * ["Memory corruption" example](#memory-corruption) below for an
540 * corruption. 540 * illustration of how). Therefore, such assignments are considered
541 * `@system`.
541 * 542 *
542 * An individual assignment can be `@trusted` if the caller can 543 * An individual assignment can be `@trusted` if the caller can
543 * guarantee that there are no outstanding references to $(I any) 544 * guarantee that there are no outstanding references to any `SumType`
544 * of the `SumType`'s members when the assignment occurs. 545 * members that contain pointers or references at the time the
546 * assignment occurs.
547 *
548 * Examples:
549 *
550 * $(DIVID memory-corruption, $(H3 Memory corruption))
551 *
552 * This example shows how assignment to a `SumType` can be used to
553 * cause memory corruption in `@system` code. In `@safe` code, the
554 * assignment `s = 123` would not be allowed.
555 *
556 * ---
557 * SumType!(int*, int) s = new int;
558 * s.tryMatch!(
559 * (ref int* p) {
560 * s = 123; // overwrites `p`
561 * return *p; // undefined behavior
562 * }
563 * );
564 * ---
545 */ 565 */
546 ref SumType opAssign(T rhs); 566 ref SumType opAssign(T rhs);
547 } 567 }
@@ -553,14 +573,35 @@ public:
553 /** 573 /**
554 * Assigns a value to a `SumType`. 574 * Assigns a value to a `SumType`.
555 * 575 *
556 * Assigning to a `SumType` is `@system` if any of the `SumType`'s 576 * If any of the `SumType`'s members other than the one being assigned
557 * $(I other) members contain pointers or references, since those 577 * to contain pointers or references, it is possible for the assignment
558 * members may be reachable through external references, and 578 * to cause memory corruption (see the
559 * overwriting them could therefore lead to memory corruption. 579 * ["Memory corruption" example](#memory-corruption) below for an
580 * illustration of how). Therefore, such assignments are considered
581 * `@system`.
560 * 582 *
561 * An individual assignment can be `@trusted` if the caller can 583 * An individual assignment can be `@trusted` if the caller can
562 * guarantee that, when the assignment occurs, there are no 584 * guarantee that there are no outstanding references to any `SumType`
563 * outstanding references to any such members. 585 * members that contain pointers or references at the time the
586 * assignment occurs.
587 *
588 * Examples:
589 *
590 * $(DIVID memory-corruption, $(H3 Memory corruption))
591 *
592 * This example shows how assignment to a `SumType` can be used to
593 * cause memory corruption in `@system` code. In `@safe` code, the
594 * assignment `s = 123` would not be allowed.
595 *
596 * ---
597 * SumType!(int*, int) s = new int;
598 * s.tryMatch!(
599 * (ref int* p) {
600 * s = 123; // overwrites `p`
601 * return *p; // undefined behavior
602 * }
603 * );
604 * ---
564 */ 605 */
565 ref SumType opAssign(T rhs) 606 ref SumType opAssign(T rhs)
566 { 607 {
@@ -1528,7 +1569,27 @@ private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);
1528} 1569}
1529 1570
1530/// True if `T` is a [SumType] or implicitly converts to one, otherwise false. 1571/// True if `T` is a [SumType] or implicitly converts to one, otherwise false.
1531enum bool isSumType(T) = is(T : SumType!Args, Args...); 1572template isSumType(T)
1573{
1574 static if (is(T : SumType!Args, Args...))
1575 {
1576 enum isSumType = true;
1577 }
1578 else static if (is(T == struct) && __traits(getAliasThis, T).length > 0)
1579 {
1580 // Workaround for https://issues.dlang.org/show_bug.cgi?id=21975
1581 import std.traits : ReturnType;
1582
1583 alias AliasThisType = ReturnType!((T t) =>
1584 __traits(getMember, t, __traits(getAliasThis, T)[0])
1585 );
1586 enum isSumType = .isSumType!AliasThisType;
1587 }
1588 else
1589 {
1590 enum isSumType = false;
1591 }
1592}
1532 1593
1533/// 1594///
1534@safe unittest 1595@safe unittest
@@ -1549,6 +1610,25 @@ enum bool isSumType(T) = is(T : SumType!Args, Args...);
1549 assert(!isSumType!ContainsSumType); 1610 assert(!isSumType!ContainsSumType);
1550} 1611}
1551 1612
1613@safe unittest
1614{
1615 static struct AliasThisVar(T)
1616 {
1617 SumType!T payload;
1618 alias payload this;
1619 }
1620
1621 static struct AliasThisFunc(T)
1622 {
1623 SumType!T payload;
1624 ref get() { return payload; }
1625 alias get this;
1626 }
1627
1628 static assert(isSumType!(AliasThisVar!int));
1629 static assert(isSumType!(AliasThisFunc!int));
1630}
1631
1552/** 1632/**
1553 * Calls a type-appropriate function with the value held in a [SumType]. 1633 * Calls a type-appropriate function with the value held in a [SumType].
1554 * 1634 *