mirror of
https://github.com/dlang/dmd.git
synced 2025-04-25 20:50:41 +03:00
Fix issue #17503 - Associative Arrays improperly register a GC-allocated TypeInfo for element cleanup
- let the compiler generate type info for the AA Entry and add it to TypeInfo_AssociativeArray - strip modifiers for index and next in TypeInfo_AssociativeArray to make it agnostic to changes by the glue layer
This commit is contained in:
parent
4d4d09ab33
commit
7d6fff7bcf
20 changed files with 153 additions and 161 deletions
|
@ -2832,6 +2832,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
|
|||
(*ae.keys)[i] = ex;
|
||||
}
|
||||
ae.type = t;
|
||||
semanticTypeInfo(sc, ae.type);
|
||||
return ae;
|
||||
}
|
||||
return visit(e);
|
||||
|
|
|
@ -1548,6 +1548,8 @@ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration
|
|||
*/
|
||||
extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration
|
||||
{
|
||||
Type entry; // type of TypeInfo_AssociativeArray.Entry!(t.index, t.next)
|
||||
|
||||
extern (D) this(Type tinfo)
|
||||
{
|
||||
super(tinfo);
|
||||
|
|
|
@ -11168,6 +11168,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
exp.e2 = e2x;
|
||||
t1 = e1x.type.toBasetype();
|
||||
}
|
||||
else if (t1.ty == Taarray)
|
||||
{
|
||||
// when assigning a constant, the need for TypeInfo might change
|
||||
semanticTypeInfo(sc, t1);
|
||||
}
|
||||
/* Check the mutability of e1.
|
||||
*/
|
||||
if (auto ale = exp.e1.isArrayLengthExp())
|
||||
|
@ -13307,8 +13312,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
}
|
||||
|
||||
if (exp.e1.type.toBasetype().ty == Taarray)
|
||||
{
|
||||
semanticTypeInfo(sc, exp.e1.type.toBasetype());
|
||||
|
||||
getTypeInfoType(exp.loc, exp.e1.type, sc);
|
||||
}
|
||||
|
||||
if (!target.isVectorOpSupported(t1, exp.op, t2))
|
||||
{
|
||||
|
@ -16849,6 +16856,9 @@ void semanticTypeInfo(Scope* sc, Type t)
|
|||
{
|
||||
semanticTypeInfo(sc, t.index);
|
||||
semanticTypeInfo(sc, t.next);
|
||||
|
||||
if (global.params.useTypeInfo)
|
||||
getTypeInfoType(t.loc, t, sc);
|
||||
}
|
||||
|
||||
void visitStruct(TypeStruct t)
|
||||
|
|
|
@ -166,6 +166,7 @@ immutable Msgtable[] msgtable =
|
|||
{ "xopCmp", "__xopCmp" },
|
||||
{ "xtoHash", "__xtoHash" },
|
||||
{ "__tmpfordtor" },
|
||||
{ "Entry" },
|
||||
|
||||
{ "LINE", "__LINE__" },
|
||||
{ "FILE", "__FILE__" },
|
||||
|
|
|
@ -852,6 +852,8 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor
|
|||
loweredExp = loweredExp.ctfeInterpret();
|
||||
|
||||
aaExp.lowering = loweredExp;
|
||||
|
||||
semanticTypeInfo(sc, loweredExp.type);
|
||||
}
|
||||
|
||||
// https://issues.dlang.org/show_bug.cgi?id=24602
|
||||
|
|
|
@ -1348,7 +1348,7 @@ private extern (C++) class TypeInfoDtVisitor : Visitor
|
|||
override void visit(TypeInfoAssociativeArrayDeclaration d)
|
||||
{
|
||||
//printf("TypeInfoAssociativeArrayDeclaration.toDt()\n");
|
||||
verifyStructSize(Type.typeinfoassociativearray, 4 * target.ptrsize);
|
||||
verifyStructSize(Type.typeinfoassociativearray, 5 * target.ptrsize);
|
||||
|
||||
dtb.xoff(toVtblSymbol(Type.typeinfoassociativearray), 0); // vtbl for TypeInfo_AssociativeArray
|
||||
if (Type.typeinfoassociativearray.hasMonitor())
|
||||
|
@ -1361,6 +1361,9 @@ private extern (C++) class TypeInfoDtVisitor : Visitor
|
|||
|
||||
TypeInfo_toObjFile(null, d.loc, tc.index);
|
||||
dtb.xoff(toSymbol(tc.index.vtinfo), 0); // TypeInfo for array of type
|
||||
|
||||
TypeInfo_toObjFile(null, d.loc, d.entry);
|
||||
dtb.xoff(toSymbol(d.entry.vtinfo), 0); // TypeInfo for key,value-pair
|
||||
}
|
||||
|
||||
override void visit(TypeInfoFunctionDeclaration d)
|
||||
|
|
|
@ -7023,6 +7023,39 @@ Type sharedWildConstOf(Type type)
|
|||
return t;
|
||||
}
|
||||
|
||||
Type nakedOf(Type type)
|
||||
{
|
||||
//printf("Type::nakedOf() %p, %s\n", type, type.toChars());
|
||||
if (type.mod == 0)
|
||||
return type;
|
||||
if (type.mcache) with(type.mcache)
|
||||
{
|
||||
// the cache has the naked type at the "identity" position, try to find it
|
||||
if (cto && cto.mod == 0)
|
||||
return cto;
|
||||
if (ito && ito.mod == 0)
|
||||
return ito;
|
||||
if (sto && sto.mod == 0)
|
||||
return sto;
|
||||
if (scto && scto.mod == 0)
|
||||
return scto;
|
||||
if (wto && wto.mod == 0)
|
||||
return wto;
|
||||
if (wcto && wcto.mod == 0)
|
||||
return wcto;
|
||||
if (swto && swto.mod == 0)
|
||||
return swto;
|
||||
if (swcto && swcto.mod == 0)
|
||||
return swcto;
|
||||
}
|
||||
Type t = type.nullAttributes();
|
||||
t.mod = 0;
|
||||
t = t.merge();
|
||||
t.fixTo(type);
|
||||
//printf("\t%p %s\n", t, t.toChars());
|
||||
return t;
|
||||
}
|
||||
|
||||
Type unqualify(Type type, uint m)
|
||||
{
|
||||
Type t = type.mutableOf().unSharedOf();
|
||||
|
|
|
@ -22,6 +22,7 @@ import dmd.expression;
|
|||
import dmd.globals;
|
||||
import dmd.location;
|
||||
import dmd.mtype;
|
||||
import dmd.typesem;
|
||||
import core.stdc.stdio;
|
||||
|
||||
/****************************************************
|
||||
|
@ -67,6 +68,9 @@ bool genTypeInfo(Expression e, Loc loc, Type torig, Scope* sc)
|
|||
|
||||
import dmd.typesem : merge2;
|
||||
Type t = torig.merge2(); // do this since not all Type's are merge'd
|
||||
if (t.ty == Taarray)
|
||||
t = makeNakedAssociativeArray(cast(TypeAArray)t);
|
||||
|
||||
bool needsCodegen = false;
|
||||
if (!t.vtinfo)
|
||||
{
|
||||
|
@ -79,7 +83,7 @@ bool genTypeInfo(Expression e, Loc loc, Type torig, Scope* sc)
|
|||
else if (t.isWild())
|
||||
t.vtinfo = TypeInfoWildDeclaration.create(t);
|
||||
else
|
||||
t.vtinfo = getTypeInfoDeclaration(t);
|
||||
t.vtinfo = getTypeInfoDeclaration(t, sc);
|
||||
assert(t.vtinfo);
|
||||
|
||||
// ClassInfos are generated as part of ClassDeclaration codegen
|
||||
|
@ -115,7 +119,7 @@ extern (C++) Type getTypeInfoType(Loc loc, Type t, Scope* sc)
|
|||
return t.vtinfo.type;
|
||||
}
|
||||
|
||||
private TypeInfoDeclaration getTypeInfoDeclaration(Type t)
|
||||
private TypeInfoDeclaration getTypeInfoDeclaration(Type t, Scope* sc)
|
||||
{
|
||||
//printf("Type::getTypeInfoDeclaration() %s\n", t.toChars());
|
||||
switch (t.ty)
|
||||
|
@ -127,7 +131,7 @@ private TypeInfoDeclaration getTypeInfoDeclaration(Type t)
|
|||
case Tsarray:
|
||||
return TypeInfoStaticArrayDeclaration.create(t);
|
||||
case Taarray:
|
||||
return TypeInfoAssociativeArrayDeclaration.create(t);
|
||||
return getTypeInfoAssocArrayDeclaration(cast(TypeAArray)t, sc);
|
||||
case Tstruct:
|
||||
return TypeInfoStructDeclaration.create(t);
|
||||
case Tvector:
|
||||
|
@ -151,6 +155,69 @@ private TypeInfoDeclaration getTypeInfoDeclaration(Type t)
|
|||
}
|
||||
}
|
||||
|
||||
/******************************************
|
||||
* Instantiate TypeInfoAssociativeArrayDeclaration and fill
|
||||
* the entry with TypeInfo_AssociativeArray.Entry!(t.index, t.next)
|
||||
*
|
||||
* Params:
|
||||
* t = TypeAArray to generate TypeInfo_AssociativeArray for
|
||||
* sc = context
|
||||
* Returns:
|
||||
* a TypeInfoAssociativeArrayDeclaration with field entry initialized
|
||||
*/
|
||||
TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc)
|
||||
{
|
||||
import dmd.arraytypes;
|
||||
import dmd.expressionsem;
|
||||
import dmd.id;
|
||||
|
||||
assert(sc); // must not be called in the code generation phase
|
||||
|
||||
auto ti = TypeInfoAssociativeArrayDeclaration.create(t);
|
||||
t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic
|
||||
Loc loc = t.loc;
|
||||
auto tiargs = new Objects();
|
||||
tiargs.push(t.index); // always called with naked types
|
||||
tiargs.push(t.next);
|
||||
|
||||
Expression id = new IdentifierExp(loc, Id.empty);
|
||||
id = new DotIdExp(loc, id, Id.object);
|
||||
id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray);
|
||||
auto tempinst = new DotTemplateInstanceExp(loc, id, Id.Entry, tiargs);
|
||||
auto e = expressionSemantic(tempinst, sc);
|
||||
assert(e.type);
|
||||
ti.entry = e.type;
|
||||
if (auto ts = ti.entry.isTypeStruct())
|
||||
{
|
||||
ts.sym.requestTypeInfo = true;
|
||||
if (auto tmpl = ts.sym.isInstantiated())
|
||||
tmpl.minst = sc._module.importedFrom; // ensure it get's emitted
|
||||
}
|
||||
getTypeInfoType(loc, ti.entry, sc);
|
||||
assert(ti.entry.vtinfo);
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
/******************************************
|
||||
* Find or create a TypeAArray with index and next without any modifiers
|
||||
*
|
||||
* Params:
|
||||
* t = TypeAArray to convert
|
||||
* Returns:
|
||||
* t = found type
|
||||
*/
|
||||
Type makeNakedAssociativeArray(TypeAArray t)
|
||||
{
|
||||
Type tindex = t.index.toBasetype().nakedOf();
|
||||
Type tnext = t.next.toBasetype().nakedOf();
|
||||
if (tindex == t.index && tnext == t.next)
|
||||
return t;
|
||||
|
||||
t = new TypeAArray(tnext, tindex);
|
||||
return t.merge();
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Returns:
|
||||
* true if any part of type t is speculative.
|
||||
|
|
|
@ -44,7 +44,7 @@ struct Impl
|
|||
Bucket[] buckets;
|
||||
uint used;
|
||||
uint deleted;
|
||||
TypeInfo_Struct entryTI;
|
||||
const(TypeInfo) entryTI;
|
||||
uint firstUsed;
|
||||
immutable uint keysz;
|
||||
immutable uint valsz;
|
||||
|
|
|
@ -1324,8 +1324,16 @@ class TypeInfo_AssociativeArray : TypeInfo
|
|||
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
|
||||
override @property uint flags() nothrow pure const { return 1; }
|
||||
|
||||
// TypeInfo entry is generated from the type of this template to help rt/aaA.d
|
||||
static struct Entry(K, V)
|
||||
{
|
||||
K key;
|
||||
V value;
|
||||
}
|
||||
|
||||
TypeInfo value;
|
||||
TypeInfo key;
|
||||
TypeInfo entry;
|
||||
|
||||
override @property size_t talign() nothrow pure const
|
||||
{
|
||||
|
|
|
@ -66,13 +66,13 @@ private:
|
|||
if ((ti.key.flags | ti.value.flags) & 1)
|
||||
flags |= Flags.hasPointers;
|
||||
|
||||
entryTI = fakeEntryTI(this, ti.key, ti.value);
|
||||
entryTI = ti.entry;
|
||||
}
|
||||
|
||||
Bucket[] buckets;
|
||||
uint used;
|
||||
uint deleted;
|
||||
TypeInfo_Struct entryTI;
|
||||
const(TypeInfo) entryTI;
|
||||
uint firstUsed;
|
||||
immutable uint keysz;
|
||||
immutable uint valsz;
|
||||
|
@ -229,15 +229,6 @@ private void* allocEntry(scope const Impl* aa, scope const void* pkey)
|
|||
return res;
|
||||
}
|
||||
|
||||
package void entryDtor(void* p, const TypeInfo_Struct sti)
|
||||
{
|
||||
// key and value type info stored after the TypeInfo_Struct by tiEntry()
|
||||
auto sizeti = __traits(classInstanceSize, TypeInfo_Struct);
|
||||
auto extra = cast(const(TypeInfo)*)(cast(void*) sti + sizeti);
|
||||
extra[0].destroy(p);
|
||||
extra[1].destroy(p + talign(extra[0].tsize, extra[1].talign));
|
||||
}
|
||||
|
||||
private bool hasDtor(const TypeInfo ti) pure nothrow
|
||||
{
|
||||
import rt.lifetime : unqualify;
|
||||
|
@ -258,132 +249,6 @@ private immutable(void)* getRTInfo(const TypeInfo ti) pure nothrow
|
|||
return isNoClass ? ti.rtInfo() : rtinfoHasPointers;
|
||||
}
|
||||
|
||||
// build type info for Entry with additional key and value fields
|
||||
TypeInfo_Struct fakeEntryTI(ref Impl aa, const TypeInfo keyti, const TypeInfo valti) nothrow
|
||||
{
|
||||
import rt.lifetime : unqualify;
|
||||
|
||||
auto kti = unqualify(keyti);
|
||||
auto vti = unqualify(valti);
|
||||
|
||||
// figure out whether RTInfo has to be generated (indicated by rtisize > 0)
|
||||
enum pointersPerWord = 8 * (void*).sizeof * (void*).sizeof;
|
||||
auto rtinfo = rtinfoNoPointers;
|
||||
size_t rtisize = 0;
|
||||
immutable(size_t)* keyinfo = void;
|
||||
immutable(size_t)* valinfo = void;
|
||||
if (aa.flags & Impl.Flags.hasPointers)
|
||||
{
|
||||
// classes are references
|
||||
keyinfo = cast(immutable(size_t)*) getRTInfo(keyti);
|
||||
valinfo = cast(immutable(size_t)*) getRTInfo(valti);
|
||||
|
||||
if (keyinfo is rtinfoHasPointers && valinfo is rtinfoHasPointers)
|
||||
rtinfo = rtinfoHasPointers;
|
||||
else
|
||||
rtisize = 1 + (aa.valoff + aa.valsz + pointersPerWord - 1) / pointersPerWord;
|
||||
}
|
||||
bool entryHasDtor = hasDtor(kti) || hasDtor(vti);
|
||||
if (rtisize == 0 && !entryHasDtor)
|
||||
return null;
|
||||
|
||||
// save kti and vti after type info for struct
|
||||
enum sizeti = __traits(classInstanceSize, TypeInfo_Struct);
|
||||
void* p = GC.malloc(sizeti + (2 + rtisize) * (void*).sizeof);
|
||||
import core.stdc.string : memcpy;
|
||||
|
||||
memcpy(p, __traits(initSymbol, TypeInfo_Struct).ptr, sizeti);
|
||||
|
||||
auto ti = cast(TypeInfo_Struct) p;
|
||||
auto extra = cast(TypeInfo*)(p + sizeti);
|
||||
extra[0] = cast() kti;
|
||||
extra[1] = cast() vti;
|
||||
|
||||
static immutable tiMangledName = "S2rt3aaA__T5EntryZ";
|
||||
ti.mangledName = tiMangledName;
|
||||
|
||||
ti.m_RTInfo = rtisize > 0 ? rtinfoEntry(aa, keyinfo, valinfo, cast(size_t*)(extra + 2), rtisize) : rtinfo;
|
||||
ti.m_flags = ti.m_RTInfo is rtinfoNoPointers ? cast(TypeInfo_Struct.StructFlags)0 : TypeInfo_Struct.StructFlags.hasPointers;
|
||||
|
||||
// we don't expect the Entry objects to be used outside of this module, so we have control
|
||||
// over the non-usage of the callback methods and other entries and can keep these null
|
||||
// xtoHash, xopEquals, xopCmp, xtoString and xpostblit
|
||||
immutable entrySize = aa.valoff + aa.valsz;
|
||||
ti.m_init = (cast(ubyte*) null)[0 .. entrySize]; // init length, but not ptr
|
||||
|
||||
if (entryHasDtor)
|
||||
{
|
||||
// xdtor needs to be built from the dtors of key and value for the GC
|
||||
ti.xdtorti = &entryDtor;
|
||||
ti.m_flags |= TypeInfo_Struct.StructFlags.isDynamicType;
|
||||
}
|
||||
|
||||
ti.m_align = cast(uint) max(kti.talign, vti.talign);
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
// build appropriate RTInfo at runtime
|
||||
immutable(void)* rtinfoEntry(ref Impl aa, immutable(size_t)* keyinfo,
|
||||
immutable(size_t)* valinfo, size_t* rtinfoData, size_t rtinfoSize) pure nothrow
|
||||
{
|
||||
enum bitsPerWord = 8 * size_t.sizeof;
|
||||
|
||||
rtinfoData[0] = aa.valoff + aa.valsz;
|
||||
rtinfoData[1..rtinfoSize] = 0;
|
||||
|
||||
void copyKeyInfo(string src)()
|
||||
{
|
||||
size_t pos = 1;
|
||||
size_t keybits = aa.keysz / (void*).sizeof;
|
||||
while (keybits >= bitsPerWord)
|
||||
{
|
||||
rtinfoData[pos] = mixin(src);
|
||||
keybits -= bitsPerWord;
|
||||
pos++;
|
||||
}
|
||||
if (keybits > 0)
|
||||
rtinfoData[pos] = mixin(src) & ((size_t(1) << keybits) - 1);
|
||||
}
|
||||
|
||||
if (keyinfo is rtinfoHasPointers)
|
||||
copyKeyInfo!"~size_t(0)"();
|
||||
else if (keyinfo !is rtinfoNoPointers)
|
||||
copyKeyInfo!"keyinfo[pos]"();
|
||||
|
||||
void copyValInfo(string src)()
|
||||
{
|
||||
size_t bitpos = aa.valoff / (void*).sizeof;
|
||||
size_t pos = 1;
|
||||
size_t dstpos = 1 + bitpos / bitsPerWord;
|
||||
size_t begoff = bitpos % bitsPerWord;
|
||||
size_t valbits = aa.valsz / (void*).sizeof;
|
||||
size_t endoff = (bitpos + valbits) % bitsPerWord;
|
||||
for (;;)
|
||||
{
|
||||
const bits = bitsPerWord - begoff;
|
||||
size_t s = mixin(src);
|
||||
rtinfoData[dstpos] |= s << begoff;
|
||||
if (begoff > 0 && valbits > bits)
|
||||
rtinfoData[dstpos+1] |= s >> bits;
|
||||
if (valbits < bitsPerWord)
|
||||
break;
|
||||
valbits -= bitsPerWord;
|
||||
dstpos++;
|
||||
pos++;
|
||||
}
|
||||
if (endoff > 0)
|
||||
rtinfoData[dstpos] &= (size_t(1) << endoff) - 1;
|
||||
}
|
||||
|
||||
if (valinfo is rtinfoHasPointers)
|
||||
copyValInfo!"~size_t(0)"();
|
||||
else if (valinfo !is rtinfoNoPointers)
|
||||
copyValInfo!"valinfo[pos]"();
|
||||
|
||||
return cast(immutable(void)*) rtinfoData;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
void test(K, V)()
|
||||
|
@ -402,12 +267,10 @@ unittest
|
|||
if (valrti is rtinfoNoPointers && keyrti is rtinfoNoPointers)
|
||||
{
|
||||
assert(!(impl.flags & Impl.Flags.hasPointers));
|
||||
assert(impl.entryTI is null);
|
||||
}
|
||||
else if (valrti is rtinfoHasPointers && keyrti is rtinfoHasPointers)
|
||||
{
|
||||
assert(impl.flags & Impl.Flags.hasPointers);
|
||||
assert(impl.entryTI is null);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -977,7 +840,10 @@ unittest
|
|||
aa1 = null;
|
||||
aa2 = null;
|
||||
aa3 = null;
|
||||
GC.runFinalizers((cast(char*)&entryDtor)[0 .. 1]);
|
||||
auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, T)).xdtor;
|
||||
GC.runFinalizers((cast(char*)dtor1)[0 .. 1]);
|
||||
auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(T, int)).xdtor;
|
||||
GC.runFinalizers((cast(char*)dtor2)[0 .. 1]);
|
||||
assert(T.dtor == 6 && T.postblit == 2);
|
||||
}
|
||||
|
||||
|
|
|
@ -1587,16 +1587,13 @@ deprecated unittest
|
|||
GC.free(blkinf.base);
|
||||
|
||||
// associative arrays
|
||||
import rt.aaA : entryDtor;
|
||||
// throw away all existing AA entries with dtor
|
||||
GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
|
||||
|
||||
S1[int] aa1;
|
||||
aa1[0] = S1(0);
|
||||
aa1[1] = S1(1);
|
||||
dtorCount = 0;
|
||||
aa1 = null;
|
||||
GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
|
||||
auto dtor1 = typeid(TypeInfo_AssociativeArray.Entry!(int, S1)).xdtor;
|
||||
GC.runFinalizers((cast(char*)dtor1)[0..1]);
|
||||
assert(dtorCount == 2);
|
||||
|
||||
int[S1] aa2;
|
||||
|
@ -1605,7 +1602,8 @@ deprecated unittest
|
|||
aa2[S1(2)] = 2;
|
||||
dtorCount = 0;
|
||||
aa2 = null;
|
||||
GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
|
||||
auto dtor2 = typeid(TypeInfo_AssociativeArray.Entry!(S1, int)).xdtor;
|
||||
GC.runFinalizers((cast(char*)dtor2)[0..1]);
|
||||
assert(dtorCount == 3);
|
||||
|
||||
S1[2][int] aa3;
|
||||
|
@ -1613,7 +1611,8 @@ deprecated unittest
|
|||
aa3[1] = [S1(1),S1(3)];
|
||||
dtorCount = 0;
|
||||
aa3 = null;
|
||||
GC.runFinalizers((cast(char*)&entryDtor)[0..1]);
|
||||
auto dtor3 = typeid(TypeInfo_AssociativeArray.Entry!(int, S1[2])).xdtor;
|
||||
GC.runFinalizers((cast(char*)dtor3)[0..1]);
|
||||
assert(dtorCount == 4);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
256 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
160 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
128 1 float profilegc.main src/profilegc.d:18
|
||||
128 1 int profilegc.main src/profilegc.d:15
|
||||
64 1 double[] profilegc.main src/profilegc.d:56
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
496 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
320 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
160 1 float profilegc.main src/profilegc.d:18
|
||||
160 1 int profilegc.main src/profilegc.d:15
|
||||
64 1 double[] profilegc.main src/profilegc.d:56
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
256 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
160 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
128 1 float profilegc.main src/profilegc.d:18
|
||||
128 1 int profilegc.main src/profilegc.d:15
|
||||
64 1 double[] profilegc.main src/profilegc.d:56
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
496 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
320 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
160 1 float profilegc.main src/profilegc.d:18
|
||||
160 1 int profilegc.main src/profilegc.d:15
|
||||
64 1 double[] profilegc.main src/profilegc.d:56
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
176 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
160 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
128 1 float profilegc.main src/profilegc.d:18
|
||||
128 1 int profilegc.main src/profilegc.d:15
|
||||
64 1 float[] profilegc.main src/profilegc.d:42
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
496 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
320 1 immutable(char)[][int] profilegc.main src/profilegc.d:23
|
||||
160 1 float profilegc.main src/profilegc.d:18
|
||||
160 1 int profilegc.main src/profilegc.d:15
|
||||
64 1 double[] profilegc.main src/profilegc.d:56
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
256 1 immutable(char)[][int] profilegc.main src\profilegc.d:23
|
||||
160 1 immutable(char)[][int] profilegc.main src\profilegc.d:23
|
||||
128 1 float profilegc.main src\profilegc.d:18
|
||||
128 1 int profilegc.main src\profilegc.d:15
|
||||
64 1 double[] profilegc.main src\profilegc.d:56
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bytes allocated, allocations, type, function, file:line
|
||||
496 1 immutable(char)[][int] profilegc.main src\profilegc.d:23
|
||||
320 1 immutable(char)[][int] profilegc.main src\profilegc.d:23
|
||||
160 1 float profilegc.main src\profilegc.d:18
|
||||
160 1 int profilegc.main src\profilegc.d:15
|
||||
64 1 double[] profilegc.main src\profilegc.d:56
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue