gcc/libphobos/libdruntime/object.d
Iain Buclaw b3f58f87d7 d: Merge upstream dmd ad8412530, druntime fd9a4544, phobos 495e835c2.
D front-end changes:

    - Import dmd v2.098.1
    - Remove calling of _d_delstruct from code generator.

Druntime changes:

    - Import druntime v2.098.1

Phobos changes:

    - Import phobos v2.098.1

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd ad8412530.
	* expr.cc (ExprVisitor::visit (DeleteExp *)): Remove code generation
	of _d_delstruct.
	* runtime.def (DELSTRUCT): Remove.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime fd9a4544.
	* src/MERGE: Merge upstream phobos 495e835c2.
2021-12-20 19:29:43 +01:00

4849 lines
124 KiB
D

/**
* $(SCRIPT inhibitQuickIndex = 1;)
* $(DIVC quickindex,
* $(BOOKTABLE,
* $(TR $(TH Category) $(TH Symbols))
* $(TR $(TD Arrays) $(TD
* $(MYREF assumeSafeAppend)
* $(MYREF capacity)
* $(MYREF idup)
* $(MYREF reserve)
* ))
* $(TR $(TD Associative arrays) $(TD
* $(MYREF byKey)
* $(MYREF byKeyValue)
* $(MYREF byValue)
* $(MYREF clear)
* $(MYREF get)
* $(MYREF keys)
* $(MYREF rehash)
* $(MYREF require)
* $(MYREF update)
* $(MYREF values)
* ))
* $(TR $(TD General) $(TD
* $(MYREF destroy)
* $(MYREF dup)
* $(MYREF hashOf)
* $(MYREF opEquals)
* ))
* $(TR $(TD Types) $(TD
* $(MYREF Error)
* $(MYREF Exception)
* $(MYREF noreturn)
* $(MYREF Object)
* $(MYREF Throwable)
* ))
* $(TR $(TD Type info) $(TD
* $(MYREF Interface)
* $(MYREF ModuleInfo)
* $(MYREF OffsetTypeInfo)
* $(MYREF RTInfoImpl)
* $(MYREF rtinfoNoPointers)
* $(MYREF TypeInfo)
* $(MYREF TypeInfo_Class)
* ))
* ))
*
* Forms the symbols available to all D programs. Includes Object, which is
* the root of the class object hierarchy. This module is implicitly
* imported.
*
* Copyright: Copyright Digital Mars 2000 - 2011.
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly
* Source: $(DRUNTIMESRC object.d)
*/
module object;
alias size_t = typeof(int.sizeof);
alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0);
alias sizediff_t = ptrdiff_t; // For backwards compatibility only.
alias noreturn = typeof(*null); /// bottom type
alias hash_t = size_t; // For backwards compatibility only.
alias equals_t = bool; // For backwards compatibility only.
alias string = immutable(char)[];
alias wstring = immutable(wchar)[];
alias dstring = immutable(dchar)[];
version (D_ObjectiveC)
{
deprecated("explicitly import `selector` instead using: `import core.attribute : selector;`")
public import core.attribute : selector;
}
version (Posix) public import core.attribute : gnuAbiTag;
// Some ABIs use a complex varargs implementation requiring TypeInfo.argTypes().
version (GNU)
{
// No TypeInfo-based core.vararg.va_arg().
}
else version (X86_64)
{
version (DigitalMars) version = WithArgTypes;
else version (Windows) { /* no need for Win64 ABI */ }
else version = WithArgTypes;
}
else version (AArch64)
{
// Apple uses a trivial varargs implementation
version (OSX) {}
else version (iOS) {}
else version (TVOS) {}
else version (WatchOS) {}
else version = WithArgTypes;
}
/**
* All D class objects inherit from Object.
*/
class Object
{
/**
* Convert Object to a human readable string.
*/
string toString()
{
return typeid(this).name;
}
@system unittest
{
enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
enum fqn_unittest = "object.Object." ~ unittest_sym_name; // object.__unittest_LX_CY
class C {}
Object obj = new Object;
C c = new C;
assert(obj.toString() == "object.Object");
assert(c.toString() == fqn_unittest ~ ".C");
}
/**
* Compute hash function for Object.
*/
size_t toHash() @trusted nothrow
{
// BUG: this prevents a compacting GC from working, needs to be fixed
size_t addr = cast(size_t) cast(void*) this;
// The bottom log2((void*).alignof) bits of the address will always
// be 0. Moreover it is likely that each Object is allocated with a
// separate call to malloc. The alignment of malloc differs from
// platform to platform, but rather than having special cases for
// each platform it is safe to use a shift of 4. To minimize
// collisions in the low bits it is more important for the shift to
// not be too small than for the shift to not be too big.
return addr ^ (addr >>> 4);
}
/**
* Compare with another Object obj.
* Returns:
* $(TABLE
* $(TR $(TD this < obj) $(TD < 0))
* $(TR $(TD this == obj) $(TD 0))
* $(TR $(TD this > obj) $(TD > 0))
* )
*/
int opCmp(Object o)
{
// BUG: this prevents a compacting GC from working, needs to be fixed
//return cast(int)cast(void*)this - cast(int)cast(void*)o;
throw new Exception("need opCmp for class " ~ typeid(this).name);
//return this !is o;
}
@system unittest
{
Object obj = new Object;
bool gotCaught;
try
{
obj.opCmp(new Object);
}
catch (Exception e)
{
gotCaught = true;
assert(e.msg == "need opCmp for class object.Object");
}
assert(gotCaught);
}
/**
* Test whether $(D this) is equal to $(D o).
* The default implementation only compares by identity (using the $(D is) operator).
* Generally, overrides for $(D opEquals) should attempt to compare objects by their contents.
*/
bool opEquals(Object o)
{
return this is o;
}
interface Monitor
{
void lock();
void unlock();
}
/**
* Create instance of class specified by the fully qualified name
* classname.
* The class must either have no constructors or have
* a default constructor.
* Returns:
* null if failed
* Example:
* ---
* module foo.bar;
*
* class C
* {
* this() { x = 10; }
* int x;
* }
*
* void main()
* {
* auto c = cast(C)Object.factory("foo.bar.C");
* assert(c !is null && c.x == 10);
* }
* ---
*/
static Object factory(string classname)
{
auto ci = TypeInfo_Class.find(classname);
if (ci)
{
return ci.create();
}
return null;
}
@system unittest
{
Object valid_obj = Object.factory("object.Object");
Object invalid_obj = Object.factory("object.__this_class_doesnt_exist__");
assert(valid_obj !is null);
assert(invalid_obj is null);
}
}
bool opEquals(Object lhs, Object rhs)
{
// If aliased to the same object or both null => equal
if (lhs is rhs) return true;
// If either is null => non-equal
if (lhs is null || rhs is null) return false;
if (!lhs.opEquals(rhs)) return false;
// If same exact type => one call to method opEquals
if (typeid(lhs) is typeid(rhs) ||
!__ctfe && typeid(lhs).opEquals(typeid(rhs)))
/* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't
(issue 7147). But CTFE also guarantees that equal TypeInfos are
always identical. So, no opEquals needed during CTFE. */
{
return true;
}
// General case => symmetric calls to method opEquals
return rhs.opEquals(lhs);
}
/************************
* Returns true if lhs and rhs are equal.
*/
bool opEquals(const Object lhs, const Object rhs)
{
// A hack for the moment.
return opEquals(cast()lhs, cast()rhs);
}
/// If aliased to the same object or both null => equal
@system unittest
{
class F { int flag; this(int flag) { this.flag = flag; } }
F f;
assert(f == f); // both null
f = new F(1);
assert(f == f); // both aliased to the same object
}
/// If either is null => non-equal
@system unittest
{
class F { int flag; this(int flag) { this.flag = flag; } }
F f;
assert(!(new F(0) == f));
assert(!(f == new F(0)));
}
/// If same exact type => one call to method opEquals
@system unittest
{
class F
{
int flag;
this(int flag)
{
this.flag = flag;
}
override bool opEquals(const Object o)
{
return flag == (cast(F) o).flag;
}
}
F f;
assert(new F(0) == new F(0));
assert(!(new F(0) == new F(1)));
}
/// General case => symmetric calls to method opEquals
@system unittest
{
int fEquals, gEquals;
class Base
{
int flag;
this(int flag)
{
this.flag = flag;
}
}
class F : Base
{
this(int flag) { super(flag); }
override bool opEquals(const Object o)
{
fEquals++;
return flag == (cast(Base) o).flag;
}
}
class G : Base
{
this(int flag) { super(flag); }
override bool opEquals(const Object o)
{
gEquals++;
return flag == (cast(Base) o).flag;
}
}
assert(new F(1) == new G(1));
assert(fEquals == 1);
assert(gEquals == 1);
}
// To cover const Object opEquals
@system unittest
{
const Object obj1 = new Object;
const Object obj2 = new Object;
assert(obj1 == obj1);
assert(obj1 != obj2);
}
private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow;
void setSameMutex(shared Object ownee, shared Object owner)
{
_d_setSameMutex(ownee, owner);
}
@system unittest
{
shared Object obj1 = new Object;
synchronized class C
{
void bar() {}
}
shared C obj2 = new shared(C);
obj2.bar();
assert(obj1.__monitor != obj2.__monitor);
assert(obj1.__monitor is null);
setSameMutex(obj1, obj2);
assert(obj1.__monitor == obj2.__monitor);
assert(obj1.__monitor !is null);
}
/**
* Information about an interface.
* When an object is accessed via an interface, an Interface* appears as the
* first entry in its vtbl.
*/
struct Interface
{
TypeInfo_Class classinfo; /// .classinfo for this interface (not for containing class)
void*[] vtbl;
size_t offset; /// offset to Interface 'this' from Object 'this'
}
/**
* Array of pairs giving the offset and type information for each
* member in an aggregate.
*/
struct OffsetTypeInfo
{
size_t offset; /// Offset of member from start of object
TypeInfo ti; /// TypeInfo for this member
}
/**
* Runtime type information about a type.
* Can be retrieved for any type using a
* $(GLINK2 expression,TypeidExpression, TypeidExpression).
*/
class TypeInfo
{
override string toString() const @safe nothrow
{
return typeid(this).name;
}
override size_t toHash() @trusted const nothrow
{
return hashOf(this.toString());
}
override int opCmp(Object rhs)
{
if (this is rhs)
return 0;
auto ti = cast(TypeInfo) rhs;
if (ti is null)
return 1;
return __cmp(this.toString(), ti.toString());
}
@system unittest
{
assert(typeid(void) <= typeid(void));
assert(typeid(void).opCmp(null));
assert(!typeid(void).opCmp(typeid(void)));
}
override bool opEquals(Object o)
{
/* TypeInfo instances are singletons, but duplicates can exist
* across DLL's. Therefore, comparing for a name match is
* sufficient.
*/
if (this is o)
return true;
auto ti = cast(const TypeInfo)o;
return ti && this.toString() == ti.toString();
}
@system unittest
{
auto anotherObj = new Object();
assert(typeid(void).opEquals(typeid(void)));
assert(!typeid(void).opEquals(anotherObj));
}
/**
* Computes a hash of the instance of a type.
* Params:
* p = pointer to start of instance of the type
* Returns:
* the hash
* Bugs:
* fix https://issues.dlang.org/show_bug.cgi?id=12516 e.g. by changing this to a truly safe interface.
*/
size_t getHash(scope const void* p) @trusted nothrow const
{
return hashOf(p);
}
/// Compares two instances for equality.
bool equals(in void* p1, in void* p2) const { return p1 == p2; }
/// Compares two instances for &lt;, ==, or &gt;.
int compare(in void* p1, in void* p2) const { return _xopCmp(p1, p2); }
/// Returns size of the type.
@property size_t tsize() nothrow pure const @safe @nogc { return 0; }
/// Swaps two instances of the type.
void swap(void* p1, void* p2) const
{
size_t remaining = tsize;
// If the type might contain pointers perform the swap in pointer-sized
// chunks in case a garbage collection pass interrupts this function.
if ((cast(size_t) p1 | cast(size_t) p2) % (void*).alignof == 0)
{
while (remaining >= (void*).sizeof)
{
void* tmp = *cast(void**) p1;
*cast(void**) p1 = *cast(void**) p2;
*cast(void**) p2 = tmp;
p1 += (void*).sizeof;
p2 += (void*).sizeof;
remaining -= (void*).sizeof;
}
}
for (size_t i = 0; i < remaining; i++)
{
byte t = (cast(byte *)p1)[i];
(cast(byte*)p1)[i] = (cast(byte*)p2)[i];
(cast(byte*)p2)[i] = t;
}
}
@system unittest
{
class _TypeInfo_Dummy : TypeInfo
{
override const(void)[] initializer() const { return []; }
@property override size_t tsize() nothrow pure const @safe @nogc { return tsize_val; }
size_t tsize_val;
}
auto dummy = new _TypeInfo_Dummy();
cast(void)dummy.initializer(); // For coverage completeness
int a = 2, b = -2;
dummy.swap(&a, &b);
// does nothing because tsize is 0
assert(a == 2);
assert(b == -2);
dummy.tsize_val = int.sizeof;
dummy.swap(&a, &b);
assert(a == -2);
assert(b == 2);
void* ptr_a = null, ptr_b = cast(void*)1;
dummy.tsize_val = (void*).sizeof;
dummy.swap(&ptr_a, &ptr_b);
assert(ptr_a is cast(void*)1);
assert(ptr_b is null);
}
/** Get TypeInfo for 'next' type, as defined by what kind of type this is,
null if none. */
@property inout(TypeInfo) next() nothrow pure inout @nogc { return null; }
/**
* Return default initializer. If the type should be initialized to all
* zeros, an array with a null ptr and a length equal to the type size will
* be returned. For static arrays, this returns the default initializer for
* a single element of the array, use `tsize` to get the correct size.
*/
abstract const(void)[] initializer() nothrow pure const @safe @nogc;
/** Get flags for type: 1 means GC should scan for pointers,
2 means arg of this type is passed in SIMD register(s) if available */
@property uint flags() nothrow pure const @safe @nogc { return 0; }
/// Get type information on the contents of the type; null if not available
const(OffsetTypeInfo)[] offTi() const { return null; }
/// Run the destructor on the object and all its sub-objects
void destroy(void* p) const {}
/// Run the postblit on the object and all its sub-objects
void postblit(void* p) const {}
/// Return alignment of type
@property size_t talign() nothrow pure const @safe @nogc { return tsize; }
/** Return internal info on arguments fitting into 8byte.
* See X86-64 ABI 3.2.3
*/
version (WithArgTypes) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow
{
arg1 = this;
return 0;
}
/** Return info used by the garbage collector to do precise collection.
*/
@property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return rtinfoHasPointers; } // better safe than sorry
}
@system unittest
{
class _TypeInfo_Dummy : TypeInfo
{
override const(void)[] initializer() const { return []; }
}
auto dummy = new _TypeInfo_Dummy();
cast(void)dummy.initializer(); // For coverage completeness
assert(dummy.rtInfo() is rtinfoHasPointers);
assert(typeid(void).rtInfo() is rtinfoNoPointers);
assert(dummy.tsize() == 0);
bool gotCaught;
try
{
dummy.compare(null, null);
} catch (Error e)
{
gotCaught = true;
assert(e.msg == "TypeInfo.compare is not implemented");
}
assert(gotCaught);
assert(dummy.equals(null, null));
assert(!dummy.equals(cast(void*)1, null));
}
@system unittest
{
assert(typeid(void).next() is null);
assert(typeid(void).offTi() is null);
assert(typeid(void).tsize() == 1);
version (WithArgTypes)
{
TypeInfo ti1;
TypeInfo ti2;
assert(typeid(void).argTypes(ti1, ti2) == 0);
assert(typeid(void) is ti1);
assert(ti1 !is null);
assert(ti2 is null);
}
}
@system unittest
{
class _ZypeInfo_Dummy : TypeInfo
{
override const(void)[] initializer() const { return []; }
}
auto dummy2 = new _ZypeInfo_Dummy();
cast(void)dummy2.initializer(); // For coverage completeness
assert(typeid(void) > dummy2);
assert(dummy2 < typeid(void));
}
@safe unittest
{
enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
class _TypeInfo_Dummy : TypeInfo
{
override const(void)[] initializer() const { return []; }
}
auto dummy = new _TypeInfo_Dummy();
cast(void)dummy.initializer(); // For coverage completeness
assert(dummy.toString() == fqn_unittest ~ "._TypeInfo_Dummy");
assert(dummy.toHash() == hashOf(dummy.toString()));
assert(dummy.getHash(null) == 0);
}
class TypeInfo_Enum : TypeInfo
{
override string toString() const pure { return name; }
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Enum)o;
return c && this.name == c.name &&
this.base == c.base;
}
@system unittest
{
enum E { A, B, C }
enum EE { A, B, C }
assert(typeid(E).opEquals(typeid(E)));
assert(!typeid(E).opEquals(typeid(EE)));
}
override size_t getHash(scope const void* p) const { return base.getHash(p); }
@system unittest
{
enum E { A, B, C }
E e1 = E.A;
E e2 = E.B;
assert(typeid(E).getHash(&e1) == hashOf(E.A));
assert(typeid(E).getHash(&e2) == hashOf(E.B));
enum ES : string { A = "foo", B = "bar" }
ES es1 = ES.A;
ES es2 = ES.B;
assert(typeid(ES).getHash(&es1) == hashOf("foo"));
assert(typeid(ES).getHash(&es2) == hashOf("bar"));
}
override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
@system unittest
{
enum E { A, B, C }
E e1 = E.A;
E e2 = E.B;
assert(typeid(E).equals(&e1, &e1));
assert(!typeid(E).equals(&e1, &e2));
}
override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); }
@system unittest
{
enum E { A, B, C }
E e1 = E.A;
E e2 = E.B;
assert(typeid(E).compare(&e1, &e1) == 0);
assert(typeid(E).compare(&e1, &e2) < 0);
assert(typeid(E).compare(&e2, &e1) > 0);
}
override @property size_t tsize() nothrow pure const { return base.tsize; }
@safe unittest
{
enum E { A, B, C }
enum ES : string { A = "a", B = "b", C = "c"}
assert(typeid(E).tsize == E.sizeof);
assert(typeid(ES).tsize == ES.sizeof);
assert(typeid(E).tsize != ES.sizeof);
}
override void swap(void* p1, void* p2) const { return base.swap(p1, p2); }
@system unittest
{
enum E { A, B, C }
E e1 = E.A;
E e2 = E.B;
typeid(E).swap(&e1, &e2);
assert(e1 == E.B);
assert(e2 == E.A);
}
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
@system unittest
{
enum E { A, B, C }
assert(typeid(E).next is null);
}
override @property uint flags() nothrow pure const { return base.flags; }
@safe unittest
{
enum E { A, B, C }
assert(typeid(E).flags == 0);
}
override const(OffsetTypeInfo)[] offTi() const { return base.offTi; }
@system unittest
{
enum E { A, B, C }
assert(typeid(E).offTi is null);
}
override void destroy(void* p) const { return base.destroy(p); }
override void postblit(void* p) const { return base.postblit(p); }
override const(void)[] initializer() const
{
return m_init.length ? m_init : base.initializer();
}
override @property size_t talign() nothrow pure const { return base.talign; }
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
return base.argTypes(arg1, arg2);
}
override @property immutable(void)* rtInfo() const { return base.rtInfo; }
TypeInfo base;
string name;
void[] m_init;
}
@safe unittest
{
enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
enum E { A, B, C }
enum EE { A, B, C }
assert(typeid(E).toString() == fqn_unittest ~ ".E");
}
@safe unittest // issue 12233
{
static assert(is(typeof(TypeInfo.init) == TypeInfo));
assert(TypeInfo.init is null);
}
// Please make sure to keep this in sync with TypeInfo_P (src/rt/typeinfo/ti_ptr.d)
class TypeInfo_Pointer : TypeInfo
{
override string toString() const { return m_next.toString() ~ "*"; }
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Pointer)o;
return c && this.m_next == c.m_next;
}
override size_t getHash(scope const void* p) @trusted const
{
size_t addr = cast(size_t) *cast(const void**)p;
return addr ^ (addr >> 4);
}
override bool equals(in void* p1, in void* p2) const
{
return *cast(void**)p1 == *cast(void**)p2;
}
override int compare(in void* p1, in void* p2) const
{
const v1 = *cast(void**) p1, v2 = *cast(void**) p2;
return (v1 > v2) - (v1 < v2);
}
override @property size_t tsize() nothrow pure const
{
return (void*).sizeof;
}
override const(void)[] initializer() const @trusted
{
return (cast(void *)null)[0 .. (void*).sizeof];
}
override void swap(void* p1, void* p2) const
{
void* tmp = *cast(void**)p1;
*cast(void**)p1 = *cast(void**)p2;
*cast(void**)p2 = tmp;
}
override @property inout(TypeInfo) next() nothrow pure inout { return m_next; }
override @property uint flags() nothrow pure const { return 1; }
TypeInfo m_next;
}
class TypeInfo_Array : TypeInfo
{
override string toString() const { return value.toString() ~ "[]"; }
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Array)o;
return c && this.value == c.value;
}
override size_t getHash(scope const void* p) @trusted const
{
void[] a = *cast(void[]*)p;
return getArrayHash(value, a.ptr, a.length);
}
override bool equals(in void* p1, in void* p2) const
{
void[] a1 = *cast(void[]*)p1;
void[] a2 = *cast(void[]*)p2;
if (a1.length != a2.length)
return false;
size_t sz = value.tsize;
for (size_t i = 0; i < a1.length; i++)
{
if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz))
return false;
}
return true;
}
override int compare(in void* p1, in void* p2) const
{
void[] a1 = *cast(void[]*)p1;
void[] a2 = *cast(void[]*)p2;
size_t sz = value.tsize;
size_t len = a1.length;
if (a2.length < len)
len = a2.length;
for (size_t u = 0; u < len; u++)
{
immutable int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz);
if (result)
return result;
}
return (a1.length > a2.length) - (a1.length < a2.length);
}
override @property size_t tsize() nothrow pure const
{
return (void[]).sizeof;
}
override const(void)[] initializer() const @trusted
{
return (cast(void *)null)[0 .. (void[]).sizeof];
}
override void swap(void* p1, void* p2) const
{
void[] tmp = *cast(void[]*)p1;
*cast(void[]*)p1 = *cast(void[]*)p2;
*cast(void[]*)p2 = tmp;
}
TypeInfo value;
override @property inout(TypeInfo) next() nothrow pure inout
{
return value;
}
override @property uint flags() nothrow pure const { return 1; }
override @property size_t talign() nothrow pure const
{
return (void[]).alignof;
}
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
arg1 = typeid(size_t);
arg2 = typeid(void*);
return 0;
}
override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(void[]); }
}
class TypeInfo_StaticArray : TypeInfo
{
override string toString() const
{
import core.internal.string : unsignedToTempString;
char[20] tmpBuff = void;
const lenString = unsignedToTempString(len, tmpBuff);
return (() @trusted => cast(string) (value.toString() ~ "[" ~ lenString ~ "]"))();
}
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_StaticArray)o;
return c && this.len == c.len &&
this.value == c.value;
}
override size_t getHash(scope const void* p) @trusted const
{
return getArrayHash(value, p, len);
}
override bool equals(in void* p1, in void* p2) const
{
size_t sz = value.tsize;
for (size_t u = 0; u < len; u++)
{
if (!value.equals(p1 + u * sz, p2 + u * sz))
return false;
}
return true;
}
override int compare(in void* p1, in void* p2) const
{
size_t sz = value.tsize;
for (size_t u = 0; u < len; u++)
{
immutable int result = value.compare(p1 + u * sz, p2 + u * sz);
if (result)
return result;
}
return 0;
}
override @property size_t tsize() nothrow pure const
{
return len * value.tsize;
}
override void swap(void* p1, void* p2) const
{
import core.stdc.string : memcpy;
size_t remaining = value.tsize * len;
void[size_t.sizeof * 4] buffer = void;
while (remaining > buffer.length)
{
memcpy(buffer.ptr, p1, buffer.length);
memcpy(p1, p2, buffer.length);
memcpy(p2, buffer.ptr, buffer.length);
p1 += buffer.length;
p2 += buffer.length;
remaining -= buffer.length;
}
memcpy(buffer.ptr, p1, remaining);
memcpy(p1, p2, remaining);
memcpy(p2, buffer.ptr, remaining);
}
override const(void)[] initializer() nothrow pure const
{
return value.initializer();
}
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
override @property uint flags() nothrow pure const { return value.flags; }
override void destroy(void* p) const
{
immutable sz = value.tsize;
p += sz * len;
foreach (i; 0 .. len)
{
p -= sz;
value.destroy(p);
}
}
override void postblit(void* p) const
{
immutable sz = value.tsize;
foreach (i; 0 .. len)
{
value.postblit(p);
p += sz;
}
}
TypeInfo value;
size_t len;
override @property size_t talign() nothrow pure const
{
return value.talign;
}
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
arg1 = typeid(void*);
return 0;
}
// just return the rtInfo of the element, we have no generic type T to run RTInfo!T on
override @property immutable(void)* rtInfo() nothrow pure const @safe { return value.rtInfo(); }
}
// https://issues.dlang.org/show_bug.cgi?id=21315
@system unittest
{
int[16] a, b;
foreach (int i; 0 .. 16)
{
a[i] = i;
b[i] = ~i;
}
typeid(int[16]).swap(&a, &b);
foreach (int i; 0 .. 16)
{
assert(a[i] == ~i);
assert(b[i] == i);
}
}
class TypeInfo_AssociativeArray : TypeInfo
{
override string toString() const
{
return value.toString() ~ "[" ~ key.toString() ~ "]";
}
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_AssociativeArray)o;
return c && this.key == c.key &&
this.value == c.value;
}
override bool equals(in void* p1, in void* p2) @trusted const
{
return !!_aaEqual(this, *cast(const AA*) p1, *cast(const AA*) p2);
}
override hash_t getHash(scope const void* p) nothrow @trusted const
{
return _aaGetHash(cast(AA*)p, this);
}
// BUG: need to add the rest of the functions
override @property size_t tsize() nothrow pure const
{
return (char[int]).sizeof;
}
override const(void)[] initializer() const @trusted
{
return (cast(void *)null)[0 .. (char[int]).sizeof];
}
override @property inout(TypeInfo) next() nothrow pure inout { return value; }
override @property uint flags() nothrow pure const { return 1; }
TypeInfo value;
TypeInfo key;
override @property size_t talign() nothrow pure const
{
return (char[int]).alignof;
}
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
arg1 = typeid(void*);
return 0;
}
}
class TypeInfo_Vector : TypeInfo
{
override string toString() const { return "__vector(" ~ base.toString() ~ ")"; }
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Vector)o;
return c && this.base == c.base;
}
override size_t getHash(scope const void* p) const { return base.getHash(p); }
override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); }
override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); }
override @property size_t tsize() nothrow pure const { return base.tsize; }
override void swap(void* p1, void* p2) const { return base.swap(p1, p2); }
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
override @property uint flags() nothrow pure const { return 2; /* passed in SIMD register */ }
override const(void)[] initializer() nothrow pure const
{
return base.initializer();
}
override @property size_t talign() nothrow pure const { return 16; }
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
return base.argTypes(arg1, arg2);
}
TypeInfo base;
}
class TypeInfo_Function : TypeInfo
{
override string toString() const pure @trusted
{
import core.demangle : demangleType;
alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
return cast(string) demangle(deco);
}
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Function)o;
return c && this.deco == c.deco;
}
// BUG: need to add the rest of the functions
override @property size_t tsize() nothrow pure const
{
return 0; // no size for functions
}
override const(void)[] initializer() const @safe
{
return null;
}
override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; }
TypeInfo next;
/**
* Mangled function type string
*/
string deco;
}
@safe unittest
{
abstract class C
{
void func();
void func(int a);
int func(int a, int b);
}
alias functionTypes = typeof(__traits(getVirtualFunctions, C, "func"));
assert(typeid(functionTypes[0]).toString() == "void function()");
assert(typeid(functionTypes[1]).toString() == "void function(int)");
assert(typeid(functionTypes[2]).toString() == "int function(int, int)");
}
@system unittest
{
abstract class C
{
void func();
void func(int a);
}
alias functionTypes = typeof(__traits(getVirtualFunctions, C, "func"));
Object obj = typeid(functionTypes[0]);
assert(obj.opEquals(typeid(functionTypes[0])));
assert(typeid(functionTypes[0]) == typeid(functionTypes[0]));
assert(typeid(functionTypes[0]) != typeid(functionTypes[1]));
assert(typeid(functionTypes[0]).tsize() == 0);
assert(typeid(functionTypes[0]).initializer() is null);
assert(typeid(functionTypes[0]).rtInfo() is null);
}
class TypeInfo_Delegate : TypeInfo
{
override string toString() const pure @trusted
{
import core.demangle : demangleType;
alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure;
SafeDemangleFunctionType demangle = cast(SafeDemangleFunctionType) &demangleType;
return cast(string) demangle(deco);
}
@safe unittest
{
double sqr(double x) { return x * x; }
sqr(double.init); // for coverage completeness
auto delegate_str = "double delegate(double) pure nothrow @nogc @safe";
assert(typeid(typeof(&sqr)).toString() == delegate_str);
assert(delegate_str.hashOf() == typeid(typeof(&sqr)).hashOf());
assert(typeid(typeof(&sqr)).toHash() == typeid(typeof(&sqr)).hashOf());
int g;
alias delegate_type = typeof((int a, int b) => a + b + g);
delegate_str = "int delegate(int, int) pure nothrow @nogc @safe";
assert(typeid(delegate_type).toString() == delegate_str);
assert(delegate_str.hashOf() == typeid(delegate_type).hashOf());
assert(typeid(delegate_type).toHash() == typeid(delegate_type).hashOf());
}
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Delegate)o;
return c && this.deco == c.deco;
}
@system unittest
{
double sqr(double x) { return x * x; }
int dbl(int x) { return x + x; }
sqr(double.init); // for coverage completeness
dbl(int.init); // for coverage completeness
Object obj = typeid(typeof(&sqr));
assert(obj.opEquals(typeid(typeof(&sqr))));
assert(typeid(typeof(&sqr)) == typeid(typeof(&sqr)));
assert(typeid(typeof(&dbl)) != typeid(typeof(&sqr)));
}
override size_t getHash(scope const void* p) @trusted const
{
return hashOf(*cast(void delegate()*)p);
}
override bool equals(in void* p1, in void* p2) const
{
auto dg1 = *cast(void delegate()*)p1;
auto dg2 = *cast(void delegate()*)p2;
return dg1 == dg2;
}
override int compare(in void* p1, in void* p2) const
{
auto dg1 = *cast(void delegate()*)p1;
auto dg2 = *cast(void delegate()*)p2;
if (dg1 < dg2)
return -1;
else if (dg1 > dg2)
return 1;
else
return 0;
}
override @property size_t tsize() nothrow pure const
{
alias dg = int delegate();
return dg.sizeof;
}
override const(void)[] initializer() const @trusted
{
return (cast(void *)null)[0 .. (int delegate()).sizeof];
}
override @property uint flags() nothrow pure const { return 1; }
TypeInfo next;
string deco;
override @property size_t talign() nothrow pure const
{
alias dg = int delegate();
return dg.alignof;
}
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
arg1 = typeid(void*);
arg2 = typeid(void*);
return 0;
}
override @property immutable(void)* rtInfo() nothrow pure const @safe { return RTInfo!(int delegate()); }
}
private extern (C) Object _d_newclass(const TypeInfo_Class ci);
private extern (C) int _d_isbaseof(scope TypeInfo_Class child,
scope const TypeInfo_Class parent) @nogc nothrow pure @safe; // rt.cast_
/**
* Runtime type information about a class.
* Can be retrieved from an object instance by using the
* $(DDSUBLINK spec/property,classinfo, .classinfo) property.
*/
class TypeInfo_Class : TypeInfo
{
override string toString() const pure { return name; }
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Class)o;
return c && this.name == c.name;
}
override size_t getHash(scope const void* p) @trusted const
{
auto o = *cast(Object*)p;
return o ? o.toHash() : 0;
}
override bool equals(in void* p1, in void* p2) const
{
Object o1 = *cast(Object*)p1;
Object o2 = *cast(Object*)p2;
return (o1 is o2) || (o1 && o1.opEquals(o2));
}
override int compare(in void* p1, in void* p2) const
{
Object o1 = *cast(Object*)p1;
Object o2 = *cast(Object*)p2;
int c = 0;
// Regard null references as always being "less than"
if (o1 !is o2)
{
if (o1)
{
if (!o2)
c = 1;
else
c = o1.opCmp(o2);
}
else
c = -1;
}
return c;
}
override @property size_t tsize() nothrow pure const
{
return Object.sizeof;
}
override const(void)[] initializer() nothrow pure const @safe
{
return m_init;
}
override @property uint flags() nothrow pure const { return 1; }
override @property const(OffsetTypeInfo)[] offTi() nothrow pure const
{
return m_offTi;
}
final @property auto info() @safe @nogc nothrow pure const return { return this; }
final @property auto typeinfo() @safe @nogc nothrow pure const return { return this; }
byte[] m_init; /** class static initializer
* (init.length gives size in bytes of class)
*/
string name; /// class name
void*[] vtbl; /// virtual function pointer table
Interface[] interfaces; /// interfaces this class implements
TypeInfo_Class base; /// base class
void* destructor;
void function(Object) classInvariant;
enum ClassFlags : uint
{
isCOMclass = 0x1,
noPointers = 0x2,
hasOffTi = 0x4,
hasCtor = 0x8,
hasGetMembers = 0x10,
hasTypeInfo = 0x20,
isAbstract = 0x40,
isCPPclass = 0x80,
hasDtor = 0x100,
}
ClassFlags m_flags;
void* deallocator;
OffsetTypeInfo[] m_offTi;
void function(Object) defaultConstructor; // default Constructor
immutable(void)* m_RTInfo; // data for precise GC
override @property immutable(void)* rtInfo() const { return m_RTInfo; }
/**
* Search all modules for TypeInfo_Class corresponding to classname.
* Returns: null if not found
*/
static const(TypeInfo_Class) find(const scope char[] classname)
{
foreach (m; ModuleInfo)
{
if (m)
{
//writefln("module %s, %d", m.name, m.localClasses.length);
foreach (c; m.localClasses)
{
if (c is null)
continue;
//writefln("\tclass %s", c.name);
if (c.name == classname)
return c;
}
}
}
return null;
}
/**
* Create instance of Object represented by 'this'.
*/
Object create() const
{
if (m_flags & 8 && !defaultConstructor)
return null;
if (m_flags & 64) // abstract
return null;
Object o = _d_newclass(this);
if (m_flags & 8 && defaultConstructor)
{
defaultConstructor(o);
}
return o;
}
/**
* Returns true if the class described by `child` derives from or is
* the class described by this `TypeInfo_Class`. Always returns false
* if the argument is null.
*
* Params:
* child = TypeInfo for some class
* Returns:
* true if the class described by `child` derives from or is the
* class described by this `TypeInfo_Class`.
*/
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
{
if (m_init.length)
{
// If this TypeInfo_Class represents an actual class we only need
// to check the child and its direct ancestors.
for (auto ti = cast() child; ti !is null; ti = ti.base)
if (ti is this)
return true;
return false;
}
else
{
// If this TypeInfo_Class is the .info field of a TypeInfo_Interface
// we also need to recursively check the child's interfaces.
return child !is null && _d_isbaseof(cast() child, this);
}
}
}
alias ClassInfo = TypeInfo_Class;
@safe unittest
{
// Bugzilla 14401
static class X
{
int a;
}
assert(typeid(X).initializer is typeid(X).m_init);
assert(typeid(X).initializer.length == typeid(const(X)).initializer.length);
assert(typeid(X).initializer.length == typeid(shared(X)).initializer.length);
assert(typeid(X).initializer.length == typeid(immutable(X)).initializer.length);
}
class TypeInfo_Interface : TypeInfo
{
override string toString() const pure { return info.name; }
override bool opEquals(Object o)
{
if (this is o)
return true;
auto c = cast(const TypeInfo_Interface)o;
return c && this.info.name == typeid(c).name;
}
override size_t getHash(scope const void* p) @trusted const
{
if (!*cast(void**)p)
{
return 0;
}
Interface* pi = **cast(Interface ***)*cast(void**)p;
Object o = cast(Object)(*cast(void**)p - pi.offset);
assert(o);
return o.toHash();
}
override bool equals(in void* p1, in void* p2) const
{
Interface* pi = **cast(Interface ***)*cast(void**)p1;
Object o1 = cast(Object)(*cast(void**)p1 - pi.offset);
pi = **cast(Interface ***)*cast(void**)p2;
Object o2 = cast(Object)(*cast(void**)p2 - pi.offset);
return o1 == o2 || (o1 && o1.opCmp(o2) == 0);
}
override int compare(in void* p1, in void* p2) const
{
Interface* pi = **cast(Interface ***)*cast(void**)p1;
Object o1 = cast(Object)(*cast(void**)p1 - pi.offset);
pi = **cast(Interface ***)*cast(void**)p2;
Object o2 = cast(Object)(*cast(void**)p2 - pi.offset);
int c = 0;
// Regard null references as always being "less than"
if (o1 != o2)
{
if (o1)
{
if (!o2)
c = 1;
else
c = o1.opCmp(o2);
}
else
c = -1;
}
return c;
}
override @property size_t tsize() nothrow pure const
{
return Object.sizeof;
}
override const(void)[] initializer() const @trusted
{
return (cast(void *)null)[0 .. Object.sizeof];
}
override @property uint flags() nothrow pure const { return 1; }
TypeInfo_Class info;
/**
* Returns true if the class described by `child` derives from the
* interface described by this `TypeInfo_Interface`. Always returns
* false if the argument is null.
*
* Params:
* child = TypeInfo for some class
* Returns:
* true if the class described by `child` derives from the
* interface described by this `TypeInfo_Interface`.
*/
final bool isBaseOf(scope const TypeInfo_Class child) const @nogc nothrow pure @trusted
{
return child !is null && _d_isbaseof(cast() child, this.info);
}
/**
* Returns true if the interface described by `child` derives from
* or is the interface described by this `TypeInfo_Interface`.
* Always returns false if the argument is null.
*
* Params:
* child = TypeInfo for some interface
* Returns:
* true if the interface described by `child` derives from or is
* the interface described by this `TypeInfo_Interface`.
*/
final bool isBaseOf(scope const TypeInfo_Interface child) const @nogc nothrow pure @trusted
{
return child !is null && _d_isbaseof(cast() child.info, this.info);
}
}
@safe unittest
{
enum unittest_sym_name = __traits(identifier, __traits(parent, (){}));
enum fqn_unittest = "object." ~ unittest_sym_name; // object.__unittest_LX_CY
interface I {}
assert(fqn_unittest ~ ".I" == typeid(I).info.name);
assert((fqn_unittest ~ ".I").hashOf() == typeid(I).hashOf());
assert(typeid(I).toHash() == typeid(I).hashOf());
}
class TypeInfo_Struct : TypeInfo
{
override string toString() const { return name; }
override size_t toHash() const
{
return hashOf(this.mangledName);
}
override bool opEquals(Object o)
{
if (this is o)
return true;
auto s = cast(const TypeInfo_Struct)o;
return s && this.mangledName == s.mangledName;
}
override size_t getHash(scope const void* p) @trusted pure nothrow const
{
assert(p);
if (xtoHash)
{
return (*xtoHash)(p);
}
else
{
return hashOf(p[0 .. initializer().length]);
}
}
override bool equals(in void* p1, in void* p2) @trusted pure nothrow const
{
import core.stdc.string : memcmp;
if (!p1 || !p2)
return false;
else if (xopEquals)
{
version (GNU)
{ // BUG: GDC and DMD use different calling conventions
return (*xopEquals)(p2, p1);
}
else
return (*xopEquals)(p1, p2);
}
else if (p1 == p2)
return true;
else
// BUG: relies on the GC not moving objects
return memcmp(p1, p2, initializer().length) == 0;
}
override int compare(in void* p1, in void* p2) @trusted pure nothrow const
{
import core.stdc.string : memcmp;
// Regard null references as always being "less than"
if (p1 != p2)
{
if (p1)
{
if (!p2)
return true;
else if (xopCmp)
{
version (GNU)
{ // BUG: GDC and DMD use different calling conventions
return (*xopCmp)(p1, p2);
}
else
return (*xopCmp)(p2, p1);
}
else
// BUG: relies on the GC not moving objects
return memcmp(p1, p2, initializer().length);
}
else
return -1;
}
return 0;
}
override @property size_t tsize() nothrow pure const
{
return initializer().length;
}
override const(void)[] initializer() nothrow pure const @safe
{
return m_init;
}
override @property uint flags() nothrow pure const { return m_flags; }
override @property size_t talign() nothrow pure const { return m_align; }
final override void destroy(void* p) const
{
if (xdtor)
{
if (m_flags & StructFlags.isDynamicType)
(*xdtorti)(p, this);
else
(*xdtor)(p);
}
}
override void postblit(void* p) const
{
if (xpostblit)
(*xpostblit)(p);
}
string mangledName;
final @property string name() nothrow const @trusted
{
import core.demangle : demangleType;
if (mangledName is null) // e.g., opaque structs
return null;
const key = cast(const void*) this; // faster lookup than TypeInfo_Struct, at the cost of potential duplicates per binary
static string[typeof(key)] demangledNamesCache; // per thread
// not nothrow:
//return demangledNamesCache.require(key, cast(string) demangleType(mangledName));
if (auto pDemangled = key in demangledNamesCache)
return *pDemangled;
const demangled = cast(string) demangleType(mangledName);
demangledNamesCache[key] = demangled;
return demangled;
}
void[] m_init; // initializer; m_init.ptr == null if 0 initialize
@safe pure nothrow
{
size_t function(in void*) xtoHash;
bool function(in void*, in void*) xopEquals;
int function(in void*, in void*) xopCmp;
string function(in void*) xtoString;
enum StructFlags : uint
{
hasPointers = 0x1,
isDynamicType = 0x2, // built at runtime, needs type info in xdtor
}
StructFlags m_flags;
}
union
{
void function(void*) xdtor;
void function(void*, const TypeInfo_Struct ti) xdtorti;
}
void function(void*) xpostblit;
uint m_align;
override @property immutable(void)* rtInfo() nothrow pure const @safe { return m_RTInfo; }
version (WithArgTypes)
{
override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
arg1 = m_arg1;
arg2 = m_arg2;
return 0;
}
TypeInfo m_arg1;
TypeInfo m_arg2;
}
immutable(void)* m_RTInfo; // data for precise GC
}
@system unittest
{
struct S
{
bool opEquals(ref const S rhs) const
{
return false;
}
}
S s;
assert(!typeid(S).equals(&s, &s));
}
class TypeInfo_Tuple : TypeInfo
{
TypeInfo[] elements;
override string toString() const
{
string s = "(";
foreach (i, element; elements)
{
if (i)
s ~= ',';
s ~= element.toString();
}
s ~= ")";
return s;
}
override bool opEquals(Object o)
{
if (this is o)
return true;
auto t = cast(const TypeInfo_Tuple)o;
if (t && elements.length == t.elements.length)
{
for (size_t i = 0; i < elements.length; i++)
{
if (elements[i] != t.elements[i])
return false;
}
return true;
}
return false;
}
override size_t getHash(scope const void* p) const
{
assert(0);
}
override bool equals(in void* p1, in void* p2) const
{
assert(0);
}
override int compare(in void* p1, in void* p2) const
{
assert(0);
}
override @property size_t tsize() nothrow pure const
{
assert(0);
}
override const(void)[] initializer() const @trusted
{
assert(0);
}
override void swap(void* p1, void* p2) const
{
assert(0);
}
override void destroy(void* p) const
{
assert(0);
}
override void postblit(void* p) const
{
assert(0);
}
override @property size_t talign() nothrow pure const
{
assert(0);
}
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
assert(0);
}
}
class TypeInfo_Const : TypeInfo
{
override string toString() const
{
return cast(string) ("const(" ~ base.toString() ~ ")");
}
//override bool opEquals(Object o) { return base.opEquals(o); }
override bool opEquals(Object o)
{
if (this is o)
return true;
if (typeid(this) != typeid(o))
return false;
auto t = cast(TypeInfo_Const)o;
return base.opEquals(t.base);
}
override size_t getHash(scope const void *p) const { return base.getHash(p); }
override bool equals(in void *p1, in void *p2) const { return base.equals(p1, p2); }
override int compare(in void *p1, in void *p2) const { return base.compare(p1, p2); }
override @property size_t tsize() nothrow pure const { return base.tsize; }
override void swap(void *p1, void *p2) const { return base.swap(p1, p2); }
override @property inout(TypeInfo) next() nothrow pure inout { return base.next; }
override @property uint flags() nothrow pure const { return base.flags; }
override const(void)[] initializer() nothrow pure const
{
return base.initializer();
}
override @property size_t talign() nothrow pure const { return base.talign; }
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{
return base.argTypes(arg1, arg2);
}
TypeInfo base;
}
class TypeInfo_Invariant : TypeInfo_Const
{
override string toString() const
{
return cast(string) ("immutable(" ~ base.toString() ~ ")");
}
}
class TypeInfo_Shared : TypeInfo_Const
{
override string toString() const
{
return cast(string) ("shared(" ~ base.toString() ~ ")");
}
}
class TypeInfo_Inout : TypeInfo_Const
{
override string toString() const
{
return cast(string) ("inout(" ~ base.toString() ~ ")");
}
}
// Contents of Moduleinfo._flags
enum
{
MIctorstart = 0x1, // we've started constructing it
MIctordone = 0x2, // finished construction
MIstandalone = 0x4, // module ctor does not depend on other module
// ctors being done first
MItlsctor = 8,
MItlsdtor = 0x10,
MIctor = 0x20,
MIdtor = 0x40,
MIxgetMembers = 0x80,
MIictor = 0x100,
MIunitTest = 0x200,
MIimportedModules = 0x400,
MIlocalClasses = 0x800,
MIname = 0x1000,
}
/*****************************************
* An instance of ModuleInfo is generated into the object file for each compiled module.
*
* It provides access to various aspects of the module.
* It is not generated for betterC.
*/
struct ModuleInfo
{
uint _flags; // MIxxxx
uint _index; // index into _moduleinfo_array[]
version (all)
{
deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.")
void opAssign(const scope ModuleInfo m) { _flags = m._flags; _index = m._index; }
}
else
{
@disable this();
}
const:
private void* addrOf(int flag) return nothrow pure @nogc
in
{
assert(flag >= MItlsctor && flag <= MIname);
assert(!(flag & (flag - 1)) && !(flag & ~(flag - 1) << 1));
}
do
{
import core.stdc.string : strlen;
void* p = cast(void*)&this + ModuleInfo.sizeof;
if (flags & MItlsctor)
{
if (flag == MItlsctor) return p;
p += typeof(tlsctor).sizeof;
}
if (flags & MItlsdtor)
{
if (flag == MItlsdtor) return p;
p += typeof(tlsdtor).sizeof;
}
if (flags & MIctor)
{
if (flag == MIctor) return p;
p += typeof(ctor).sizeof;
}
if (flags & MIdtor)
{
if (flag == MIdtor) return p;
p += typeof(dtor).sizeof;
}
if (flags & MIxgetMembers)
{
if (flag == MIxgetMembers) return p;
p += typeof(xgetMembers).sizeof;
}
if (flags & MIictor)
{
if (flag == MIictor) return p;
p += typeof(ictor).sizeof;
}
if (flags & MIunitTest)
{
if (flag == MIunitTest) return p;
p += typeof(unitTest).sizeof;
}
if (flags & MIimportedModules)
{
if (flag == MIimportedModules) return p;
p += size_t.sizeof + *cast(size_t*)p * typeof(importedModules[0]).sizeof;
}
if (flags & MIlocalClasses)
{
if (flag == MIlocalClasses) return p;
p += size_t.sizeof + *cast(size_t*)p * typeof(localClasses[0]).sizeof;
}
if (true || flags & MIname) // always available for now
{
if (flag == MIname) return p;
p += strlen(cast(immutable char*)p);
}
assert(0);
}
@property uint index() nothrow pure @nogc { return _index; }
@property uint flags() nothrow pure @nogc { return _flags; }
/************************
* Returns:
* module constructor for thread locals, `null` if there isn't one
*/
@property void function() tlsctor() nothrow pure @nogc
{
return flags & MItlsctor ? *cast(typeof(return)*)addrOf(MItlsctor) : null;
}
/************************
* Returns:
* module destructor for thread locals, `null` if there isn't one
*/
@property void function() tlsdtor() nothrow pure @nogc
{
return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null;
}
/*****************************
* Returns:
* address of a module's `const(MemberInfo)[] getMembers(string)` function, `null` if there isn't one
*/
@property void* xgetMembers() nothrow pure @nogc
{
return flags & MIxgetMembers ? *cast(typeof(return)*)addrOf(MIxgetMembers) : null;
}
/************************
* Returns:
* module constructor, `null` if there isn't one
*/
@property void function() ctor() nothrow pure @nogc
{
return flags & MIctor ? *cast(typeof(return)*)addrOf(MIctor) : null;
}
/************************
* Returns:
* module destructor, `null` if there isn't one
*/
@property void function() dtor() nothrow pure @nogc
{
return flags & MIdtor ? *cast(typeof(return)*)addrOf(MIdtor) : null;
}
/************************
* Returns:
* module order independent constructor, `null` if there isn't one
*/
@property void function() ictor() nothrow pure @nogc
{
return flags & MIictor ? *cast(typeof(return)*)addrOf(MIictor) : null;
}
/*************
* Returns:
* address of function that runs the module's unittests, `null` if there isn't one
*/
@property void function() unitTest() nothrow pure @nogc
{
return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null;
}
/****************
* Returns:
* array of pointers to the ModuleInfo's of modules imported by this one
*/
@property immutable(ModuleInfo*)[] importedModules() return nothrow pure @nogc
{
if (flags & MIimportedModules)
{
auto p = cast(size_t*)addrOf(MIimportedModules);
return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p];
}
return null;
}
/****************
* Returns:
* array of TypeInfo_Class references for classes defined in this module
*/
@property TypeInfo_Class[] localClasses() return nothrow pure @nogc
{
if (flags & MIlocalClasses)
{
auto p = cast(size_t*)addrOf(MIlocalClasses);
return (cast(TypeInfo_Class*)(p + 1))[0 .. *p];
}
return null;
}
/********************
* Returns:
* name of module, `null` if no name
*/
@property string name() return nothrow pure @nogc
{
import core.stdc.string : strlen;
auto p = cast(immutable char*) addrOf(MIname);
return p[0 .. strlen(p)];
}
static int opApply(scope int delegate(ModuleInfo*) dg)
{
import core.internal.traits : externDFunc;
alias moduleinfos_apply = externDFunc!("rt.minfo.moduleinfos_apply",
int function(scope int delegate(immutable(ModuleInfo*))));
// Bugzilla 13084 - enforcing immutable ModuleInfo would break client code
return moduleinfos_apply(
(immutable(ModuleInfo*)m) => dg(cast(ModuleInfo*)m));
}
}
@system unittest
{
ModuleInfo* m1;
foreach (m; ModuleInfo)
{
m1 = m;
}
}
///////////////////////////////////////////////////////////////////////////////
// Throwable
///////////////////////////////////////////////////////////////////////////////
/**
* The base class of all thrown objects.
*
* All thrown objects must inherit from Throwable. Class $(D Exception), which
* derives from this class, represents the category of thrown objects that are
* safe to catch and handle. In principle, one should not catch Throwable
* objects that are not derived from $(D Exception), as they represent
* unrecoverable runtime errors. Certain runtime guarantees may fail to hold
* when these errors are thrown, making it unsafe to continue execution after
* catching them.
*/
class Throwable : Object
{
interface TraceInfo
{
int opApply(scope int delegate(ref const(char[]))) const;
int opApply(scope int delegate(ref size_t, ref const(char[]))) const;
string toString() const;
}
string msg; /// A message describing the error.
/**
* The _file name of the D source code corresponding with
* where the error was thrown from.
*/
string file;
/**
* The _line number of the D source code corresponding with
* where the error was thrown from.
*/
size_t line;
/**
* The stack trace of where the error happened. This is an opaque object
* that can either be converted to $(D string), or iterated over with $(D
* foreach) to extract the items in the stack trace (as strings).
*/
TraceInfo info;
/**
* A reference to the _next error in the list. This is used when a new
* $(D Throwable) is thrown from inside a $(D catch) block. The originally
* caught $(D Exception) will be chained to the new $(D Throwable) via this
* field.
*/
private Throwable nextInChain;
private uint _refcount; // 0 : allocated by GC
// 1 : allocated by _d_newThrowable()
// 2.. : reference count + 1
/**
* Returns:
* A reference to the _next error in the list. This is used when a new
* $(D Throwable) is thrown from inside a $(D catch) block. The originally
* caught $(D Exception) will be chained to the new $(D Throwable) via this
* field.
*/
@property inout(Throwable) next() @safe inout return scope pure nothrow @nogc { return nextInChain; }
/**
* Replace next in chain with `tail`.
* Use `chainTogether` instead if at all possible.
*/
@property void next(Throwable tail) @safe scope pure nothrow @nogc
{
if (tail && tail._refcount)
++tail._refcount; // increment the replacement *first*
auto n = nextInChain;
nextInChain = null; // sever the tail before deleting it
if (n && n._refcount)
_d_delThrowable(n); // now delete the old tail
nextInChain = tail; // and set the new tail
}
/**
* Returns:
* mutable reference to the reference count, which is
* 0 - allocated by the GC, 1 - allocated by _d_newThrowable(),
* and >=2 which is the reference count + 1
* Note:
* Marked as `@system` to discourage casual use of it.
*/
@system @nogc final pure nothrow ref uint refcount() return { return _refcount; }
/**
* Loop over the chain of Throwables.
*/
int opApply(scope int delegate(Throwable) dg)
{
int result = 0;
for (Throwable t = this; t; t = t.nextInChain)
{
result = dg(t);
if (result)
break;
}
return result;
}
/**
* Append `e2` to chain of exceptions that starts with `e1`.
* Params:
* e1 = start of chain (can be null)
* e2 = second part of chain (can be null)
* Returns:
* Throwable that is at the start of the chain; null if both `e1` and `e2` are null
*/
static @__future @system @nogc pure nothrow Throwable chainTogether(return scope Throwable e1, return scope Throwable e2)
{
if (!e1)
return e2;
if (!e2)
return e1;
if (e2.refcount())
++e2.refcount();
for (auto e = e1; 1; e = e.nextInChain)
{
if (!e.nextInChain)
{
e.nextInChain = e2;
break;
}
}
return e1;
}
@nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
{
this.msg = msg;
this.nextInChain = nextInChain;
if (nextInChain && nextInChain._refcount)
++nextInChain._refcount;
//this.info = _d_traceContext();
}
@nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
{
this(msg, nextInChain);
this.file = file;
this.line = line;
//this.info = _d_traceContext();
}
@trusted nothrow ~this()
{
if (nextInChain && nextInChain._refcount)
_d_delThrowable(nextInChain);
}
/**
* Overrides $(D Object.toString) and returns the error message.
* Internally this forwards to the $(D toString) overload that
* takes a $(D_PARAM sink) delegate.
*/
override string toString()
{
string s;
toString((in buf) { s ~= buf; });
return s;
}
/**
* The Throwable hierarchy uses a toString overload that takes a
* $(D_PARAM _sink) delegate to avoid GC allocations, which cannot be
* performed in certain error situations. Override this $(D
* toString) method to customize the error message.
*/
void toString(scope void delegate(in char[]) sink) const
{
import core.internal.string : unsignedToTempString;
char[20] tmpBuff = void;
sink(typeid(this).name);
sink("@"); sink(file);
sink("("); sink(unsignedToTempString(line, tmpBuff)); sink(")");
if (msg.length)
{
sink(": "); sink(msg);
}
if (info)
{
try
{
sink("\n----------------");
foreach (t; info)
{
sink("\n"); sink(t);
}
}
catch (Throwable)
{
// ignore more errors
}
}
}
/**
* Get the message describing the error.
* Base behavior is to return the `Throwable.msg` field.
* Override to return some other error message.
*
* Returns:
* Error message
*/
@__future const(char)[] message() const
{
return this.msg;
}
}
/**
* The base class of all errors that are safe to catch and handle.
*
* In principle, only thrown objects derived from this class are safe to catch
* inside a $(D catch) block. Thrown objects not derived from Exception
* represent runtime errors that should not be caught, as certain runtime
* guarantees may not hold, making it unsafe to continue program execution.
*/
class Exception : Throwable
{
/**
* Creates a new instance of Exception. The nextInChain parameter is used
* internally and should always be $(D null) when passed by user code.
* This constructor does not automatically throw the newly-created
* Exception; the $(D throw) statement should be used for that purpose.
*/
@nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null)
{
super(msg, file, line, nextInChain);
}
@nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__)
{
super(msg, file, line, nextInChain);
}
}
///
@safe unittest
{
bool gotCaught;
try
{
throw new Exception("msg");
}
catch (Exception e)
{
gotCaught = true;
assert(e.msg == "msg");
}
assert(gotCaught);
}
@system unittest
{
{
auto e = new Exception("msg");
assert(e.file == __FILE__);
assert(e.line == __LINE__ - 2);
assert(e.nextInChain is null);
assert(e.msg == "msg");
}
{
auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42);
assert(e.file == "hello");
assert(e.line == 42);
assert(e.nextInChain !is null);
assert(e.msg == "msg");
}
{
auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
assert(e.nextInChain !is null);
assert(e.msg == "msg");
}
{
auto e = new Exception("message");
assert(e.message == "message");
}
}
/**
* The base class of all unrecoverable runtime errors.
*
* This represents the category of $(D Throwable) objects that are $(B not)
* safe to catch and handle. In principle, one should not catch Error
* objects, as they represent unrecoverable runtime errors.
* Certain runtime guarantees may fail to hold when these errors are
* thrown, making it unsafe to continue execution after catching them.
*/
class Error : Throwable
{
/**
* Creates a new instance of Error. The nextInChain parameter is used
* internally and should always be $(D null) when passed by user code.
* This constructor does not automatically throw the newly-created
* Error; the $(D throw) statement should be used for that purpose.
*/
@nogc @safe pure nothrow this(string msg, Throwable nextInChain = null)
{
super(msg, nextInChain);
bypassedException = null;
}
@nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable nextInChain = null)
{
super(msg, file, line, nextInChain);
bypassedException = null;
}
/** The first $(D Exception) which was bypassed when this Error was thrown,
or $(D null) if no $(D Exception)s were pending. */
Throwable bypassedException;
}
///
@system unittest
{
bool gotCaught;
try
{
throw new Error("msg");
}
catch (Error e)
{
gotCaught = true;
assert(e.msg == "msg");
}
assert(gotCaught);
}
@safe unittest
{
{
auto e = new Error("msg");
assert(e.file is null);
assert(e.line == 0);
assert(e.nextInChain is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
{
auto e = new Error("msg", new Exception("It's an Exception!"));
assert(e.file is null);
assert(e.line == 0);
assert(e.nextInChain !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
{
auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!"));
assert(e.file == "hello");
assert(e.line == 42);
assert(e.nextInChain !is null);
assert(e.msg == "msg");
assert(e.bypassedException is null);
}
}
extern (C)
{
// from druntime/src/rt/aaA.d
private struct AA { void* impl; }
// size_t _aaLen(in AA aa) pure nothrow @nogc;
private void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey) pure nothrow;
private void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, const size_t valsz, const scope void* pkey, out bool found) pure nothrow;
// inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey);
inout(void[]) _aaValues(inout AA aa, const size_t keysz, const size_t valsz, const TypeInfo tiValueArray) pure nothrow;
inout(void[]) _aaKeys(inout AA aa, const size_t keysz, const TypeInfo tiKeyArray) pure nothrow;
void* _aaRehash(AA* paa, const scope TypeInfo keyti) pure nothrow;
void _aaClear(AA aa) pure nothrow;
// alias _dg_t = extern(D) int delegate(void*);
// int _aaApply(AA aa, size_t keysize, _dg_t dg);
// alias _dg2_t = extern(D) int delegate(void*, void*);
// int _aaApply2(AA aa, size_t keysize, _dg2_t dg);
private struct AARange { AA impl; size_t idx; }
AARange _aaRange(AA aa) pure nothrow @nogc @safe;
bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe;
void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe;
void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe;
void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe;
int _aaEqual(scope const TypeInfo tiRaw, scope const AA aa1, scope const AA aa2);
hash_t _aaGetHash(scope const AA* aa, scope const TypeInfo tiRaw) nothrow;
/*
_d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code.
This is a typesystem hole, however this is existing hole.
Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus
copiler allowed to create AA literal with keys, which have impure unsafe toHash methods.
*/
void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) pure;
}
void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure
{
return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values);
}
alias AssociativeArray(Key, Value) = Value[Key];
/***********************************
* Removes all remaining keys and values from an associative array.
* Params:
* aa = The associative array.
*/
void clear(Value, Key)(Value[Key] aa)
{
_aaClear(*cast(AA *) &aa);
}
/* ditto */
void clear(Value, Key)(Value[Key]* aa)
{
_aaClear(*cast(AA *) aa);
}
///
@system unittest
{
auto aa = ["k1": 2];
aa.clear;
assert("k1" !in aa);
}
// Issue 20559
@system unittest
{
static class Foo
{
int[string] aa;
alias aa this;
}
auto v = new Foo();
v["Hello World"] = 42;
v.clear;
assert("Hello World" !in v);
// Test for T*
static assert(!__traits(compiles, (&v).clear));
static assert( __traits(compiles, (*(&v)).clear));
}
/***********************************
* Reorganizes the associative array in place so that lookups are more
* efficient.
* Params:
* aa = The associative array.
* Returns:
* The rehashed associative array.
*/
T rehash(T : Value[Key], Value, Key)(T aa)
{
_aaRehash(cast(AA*)&aa, typeid(Value[Key]));
return aa;
}
/* ditto */
T rehash(T : Value[Key], Value, Key)(T* aa)
{
_aaRehash(cast(AA*)aa, typeid(Value[Key]));
return *aa;
}
/* ditto */
T rehash(T : shared Value[Key], Value, Key)(T aa)
{
_aaRehash(cast(AA*)&aa, typeid(Value[Key]));
return aa;
}
/* ditto */
T rehash(T : shared Value[Key], Value, Key)(T* aa)
{
_aaRehash(cast(AA*)aa, typeid(Value[Key]));
return *aa;
}
/***********************************
* Create a new associative array of the same size and copy the contents of the
* associative array into it.
* Params:
* aa = The associative array.
*/
V[K] dup(T : V[K], K, V)(T aa)
{
//pragma(msg, "K = ", K, ", V = ", V);
// Bug10720 - check whether V is copyable
static assert(is(typeof({ V v = aa[K.init]; })),
"cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable");
V[K] result;
//foreach (k, ref v; aa)
// result[k] = v; // Bug13701 - won't work if V is not mutable
ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow
{
import core.stdc.string : memcpy;
void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k);
memcpy(pv, &v, V.sizeof);
return *cast(V*)pv;
}
foreach (k, ref v; aa)
{
static if (!__traits(hasPostblit, V))
duplicateElem(k, v);
else static if (__traits(isStaticArray, V))
_doPostblit(duplicateElem(k, v)[]);
else static if (!is(typeof(v.__xpostblit())) && is(immutable V == immutable UV, UV))
(() @trusted => *cast(UV*) &duplicateElem(k, v))().__xpostblit();
else
duplicateElem(k, v).__xpostblit();
}
return result;
}
/* ditto */
V[K] dup(T : V[K], K, V)(T* aa)
{
return (*aa).dup;
}
///
@safe unittest
{
auto aa = ["k1": 2];
auto a2 = aa.dup;
aa["k2"] = 3;
assert("k2" !in a2);
}
// this should never be made public.
private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe
{
// ensure we are dealing with a genuine AA.
static if (is(const(V[K]) == const(T)))
alias realAA = aa;
else
const(V[K]) realAA = aa;
return _aaRange(() @trusted { return *cast(AA*)&realAA; } ());
}
/***********************************
* Returns a forward range over the keys of the associative array.
* Params:
* aa = The associative array.
* Returns:
* A forward range.
*/
auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
{
import core.internal.traits : substInout;
static struct Result
{
AARange r;
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
@property ref front() @trusted
{
return *cast(substInout!K*) _aaRangeFrontKey(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
}
return Result(_aaToRange(aa));
}
/* ditto */
auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc
{
return (*aa).byKey();
}
///
@safe unittest
{
auto dict = [1: 0, 2: 0];
int sum;
foreach (v; dict.byKey)
sum += v;
assert(sum == 3);
}
/***********************************
* Returns a forward range over the values of the associative array.
* Params:
* aa = The associative array.
* Returns:
* A forward range.
*/
auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
{
import core.internal.traits : substInout;
static struct Result
{
AARange r;
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
@property ref front() @trusted
{
return *cast(substInout!V*) _aaRangeFrontValue(r);
}
void popFront() @safe { _aaRangePopFront(r); }
@property Result save() { return this; }
}
return Result(_aaToRange(aa));
}
/* ditto */
auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
{
return (*aa).byValue();
}
///
@safe unittest
{
auto dict = ["k1": 1, "k2": 2];
int sum;
foreach (v; dict.byValue)
sum += v;
assert(sum == 3);
}
/***********************************
* Returns a forward range over the key value pairs of the associative array.
* Params:
* aa = The associative array.
* Returns:
* A forward range.
*/
auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe
{
import core.internal.traits : substInout;
static struct Result
{
AARange r;
pure nothrow @nogc:
@property bool empty() @safe { return _aaRangeEmpty(r); }
@property auto front()
{
static struct Pair
{
// We save the pointers here so that the Pair we return
// won't mutate when Result.popFront is called afterwards.
private void* keyp;
private void* valp;
@property ref key() inout @trusted
{
return *cast(substInout!K*) keyp;
}
@property ref value() inout @trusted
{
return *cast(substInout!V*) valp;
}
}
return Pair(_aaRangeFrontKey(r),
_aaRangeFrontValue(r));
}
void popFront() @safe { return _aaRangePopFront(r); }
@property Result save() { return this; }
}
return Result(_aaToRange(aa));
}
/* ditto */
auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc
{
return (*aa).byKeyValue();
}
///
@safe unittest
{
auto dict = ["k1": 1, "k2": 2];
int sum;
foreach (e; dict.byKeyValue)
sum += e.value;
assert(sum == 3);
}
/***********************************
* Returns a dynamic array, the elements of which are the keys in the
* associative array.
* Params:
* aa = The associative array.
* Returns:
* A dynamic array.
*/
Key[] keys(T : Value[Key], Value, Key)(T aa) @property
{
// ensure we are dealing with a genuine AA.
static if (is(const(Value[Key]) == const(T)))
alias realAA = aa;
else
const(Value[Key]) realAA = aa;
auto res = () @trusted {
auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[]));
return *cast(Key[]*)&a;
}();
static if (__traits(hasPostblit, Key))
_doPostblit(res);
return res;
}
/* ditto */
Key[] keys(T : Value[Key], Value, Key)(T *aa) @property
{
return (*aa).keys;
}
///
@safe unittest
{
auto aa = [1: "v1", 2: "v2"];
int sum;
foreach (k; aa.keys)
sum += k;
assert(sum == 3);
}
@safe unittest
{
static struct S
{
string str;
void[][string] dict;
alias dict this;
}
auto s = S("a");
assert(s.keys.length == 0);
}
@safe unittest
{
@safe static struct Key
{
string str;
this(this) @safe {}
}
string[Key] aa;
static assert(__traits(compiles, {
void test() @safe {
const _ = aa.keys;
}
}));
}
@safe unittest
{
static struct Key
{
string str;
this(this) @system {}
}
string[Key] aa;
static assert(!__traits(compiles, {
void test() @safe {
const _ = aa.keys;
}
}));
}
/***********************************
* Returns a dynamic array, the elements of which are the values in the
* associative array.
* Params:
* aa = The associative array.
* Returns:
* A dynamic array.
*/
Value[] values(T : Value[Key], Value, Key)(T aa) @property
{
// ensure we are dealing with a genuine AA.
static if (is(const(Value[Key]) == const(T)))
alias realAA = aa;
else
const(Value[Key]) realAA = aa;
auto res = () @trusted {
auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[]));
return *cast(Value[]*)&a;
}();
static if (__traits(hasPostblit, Value))
_doPostblit(res);
return res;
}
/* ditto */
Value[] values(T : Value[Key], Value, Key)(T *aa) @property
{
return (*aa).values;
}
///
@safe unittest
{
auto aa = ["k1": 1, "k2": 2];
int sum;
foreach (e; aa.values)
sum += e;
assert(sum == 3);
}
@safe unittest
{
static struct S
{
string str;
void[][string] dict;
alias dict this;
}
auto s = S("a");
assert(s.values.length == 0);
}
@safe unittest
{
@safe static struct Value
{
string str;
this(this) @safe {}
}
Value[string] aa;
static assert(__traits(compiles, {
void test() @safe {
const _ = aa.values;
}
}));
}
@safe unittest
{
static struct Value
{
string str;
this(this) @system {}
}
Value[string] aa;
static assert(!__traits(compiles, {
void test() @safe {
const _ = aa.values;
}
}));
}
/***********************************
* Looks up key; if it exists returns corresponding value else evaluates and
* returns defaultValue.
* Params:
* aa = The associative array.
* key = The key.
* defaultValue = The default value.
* Returns:
* The value.
*/
inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue)
{
auto p = key in aa;
return p ? *p : defaultValue;
}
/* ditto */
inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue)
{
return (*aa).get(key, defaultValue);
}
@safe unittest
{
auto aa = ["k1": 1];
assert(aa.get("k1", 0) == 1);
assert(aa.get("k2", 0) == 0);
}
/***********************************
* Looks up key; if it exists returns corresponding value else evaluates
* value, adds it to the associative array and returns it.
* Params:
* aa = The associative array.
* key = The key.
* value = The required value.
* Returns:
* The value.
*/
ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init)
{
bool found;
// if key is @safe-ly copyable, `require` can infer @safe
static if (isSafeCopyable!K)
{
auto p = () @trusted
{
return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
} ();
}
else
{
auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
}
if (found)
return *p;
else
{
*p = value; // Not `return (*p = value)` since if `=` is overloaded
return *p; // this might not return a ref to the left-hand side.
}
}
///
@safe unittest
{
auto aa = ["k1": 1];
assert(aa.require("k1", 0) == 1);
assert(aa.require("k2", 0) == 0);
assert(aa["k2"] == 0);
}
// Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test.
private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); }));
/***********************************
* Looks up key; if it exists applies the update callable else evaluates the
* create callable and adds it to the associative array
* Params:
* aa = The associative array.
* key = The key.
* create = The callable to apply on create.
* update = The callable to apply on update.
*/
void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update)
if (is(typeof(create()) : V) && (is(typeof(update(aa[K.init])) : V) || is(typeof(update(aa[K.init])) == void)))
{
bool found;
// if key is @safe-ly copyable, `update` may infer @safe
static if (isSafeCopyable!K)
{
auto p = () @trusted
{
return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
} ();
}
else
{
auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found);
}
if (!found)
*p = create();
else
{
static if (is(typeof(update(*p)) == void))
update(*p);
else
*p = update(*p);
}
}
///
@system unittest
{
auto aa = ["k1": 1];
aa.update("k1", {
return -1; // create (won't be executed)
}, (ref int v) {
v += 1; // update
});
assert(aa["k1"] == 2);
aa.update("k2", {
return 0; // create
}, (ref int v) {
v = -1; // update (won't be executed)
});
assert(aa["k2"] == 0);
}
@safe unittest
{
static struct S
{
int x;
@nogc nothrow pure:
this(this) @system {}
@safe const:
// stubs
bool opEquals(S rhs) { assert(0); }
size_t toHash() { assert(0); }
}
int[string] aai;
static assert(is(typeof(() @safe { aai.require("a", 1234); })));
static assert(is(typeof(() @safe { aai.update("a", { return 1234; }, (ref int x) { x++; return x; }); })));
S[string] aas;
static assert(is(typeof(() { aas.require("a", S(1234)); })));
static assert(is(typeof(() { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); })));
static assert(!is(typeof(() @safe { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); })));
int[S] aais;
static assert(is(typeof(() { aais.require(S(1234), 1234); })));
static assert(is(typeof(() { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
static assert(!is(typeof(() @safe { aais.require(S(1234), 1234); })));
static assert(!is(typeof(() @safe { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); })));
}
@safe unittest
{
struct S0
{
int opCall(ref int v)
{
return v + 1;
}
}
struct S1
{
int opCall()()
{
return -2;
}
T opCall(T)(ref T v)
{
return v + 1;
}
}
int[string] a = ["2" : 1];
a.update("2", () => -1, S0.init);
assert(a["2"] == 2);
a.update("0", () => -1, S0.init);
assert(a["0"] == -1);
a.update("2", S1.init, S1.init);
assert(a["2"] == 3);
a.update("1", S1.init, S1.init);
assert(a["1"] == -2);
}
@system unittest
{
int[string] aa;
foreach (n; 0 .. 2)
aa.update("k1", {
return 7;
}, (ref int v) {
return v + 3;
});
assert(aa["k1"] == 10);
}
version (CoreDdoc)
{
// This lets DDoc produce better documentation.
/**
Calculates the hash value of `arg` with an optional `seed` initial value.
The result might not be equal to `typeid(T).getHash(&arg)`.
Params:
arg = argument to calculate the hash value of
seed = optional `seed` value (may be used for hash chaining)
Return: calculated hash value of `arg`
*/
size_t hashOf(T)(auto ref T arg, size_t seed)
{
static import core.internal.hash;
return core.internal.hash.hashOf(arg, seed);
}
/// ditto
size_t hashOf(T)(auto ref T arg)
{
static import core.internal.hash;
return core.internal.hash.hashOf(arg);
}
@safe unittest
{
auto h1 = "my.string".hashOf;
assert(h1 == "my.string".hashOf);
}
}
else
{
public import core.internal.hash : hashOf;
}
///
@system unittest
{
class MyObject
{
size_t myMegaHash() const @safe pure nothrow
{
return 42;
}
}
struct Test
{
int a;
string b;
MyObject c;
size_t toHash() const pure nothrow
{
size_t hash = a.hashOf();
hash = b.hashOf(hash);
size_t h1 = c.myMegaHash();
hash = h1.hashOf(hash); //Mix two hash values
return hash;
}
}
}
bool _xopEquals(in void*, in void*)
{
throw new Error("TypeInfo.equals is not implemented");
}
bool _xopCmp(in void*, in void*)
{
throw new Error("TypeInfo.compare is not implemented");
}
/******************************************
* Create RTInfo for type T
*/
template RTInfoImpl(size_t[] pointerBitmap)
{
immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[];
}
template NoPointersBitmapPayload(size_t N)
{
enum size_t[N] NoPointersBitmapPayload = 0;
}
template RTInfo(T)
{
enum pointerBitmap = __traits(getPointerBitmap, T);
static if (pointerBitmap[1 .. $] == NoPointersBitmapPayload!(pointerBitmap.length - 1))
enum RTInfo = rtinfoNoPointers;
else
enum RTInfo = RTInfoImpl!(pointerBitmap).ptr;
}
/**
* shortcuts for the precise GC, also generated by the compiler
* used instead of the actual pointer bitmap
*/
enum immutable(void)* rtinfoNoPointers = null;
enum immutable(void)* rtinfoHasPointers = cast(void*)1;
// Helper functions
private inout(TypeInfo) getElement(return scope inout TypeInfo value) @trusted pure nothrow
{
TypeInfo element = cast() value;
for (;;)
{
if (auto qualified = cast(TypeInfo_Const) element)
element = qualified.base;
else if (auto redefined = cast(TypeInfo_Enum) element)
element = redefined.base;
else if (auto staticArray = cast(TypeInfo_StaticArray) element)
element = staticArray.value;
else if (auto vector = cast(TypeInfo_Vector) element)
element = vector.base;
else
break;
}
return cast(inout) element;
}
private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, const size_t count) @trusted nothrow
{
if (!count)
return 0;
const size_t elementSize = element.tsize;
if (!elementSize)
return 0;
static bool hasCustomToHash(const scope TypeInfo value) @trusted pure nothrow
{
const element = getElement(value);
if (const struct_ = cast(const TypeInfo_Struct) element)
return !!struct_.xtoHash;
return cast(const TypeInfo_Array) element
|| cast(const TypeInfo_AssociativeArray) element
|| cast(const ClassInfo) element
|| cast(const TypeInfo_Interface) element;
}
if (!hasCustomToHash(element))
return hashOf(ptr[0 .. elementSize * count]);
size_t hash = 0;
foreach (size_t i; 0 .. count)
hash = hashOf(element.getHash(ptr + i * elementSize), hash);
return hash;
}
/// Provide the .dup array property.
@property auto dup(T)(T[] a)
if (!is(const(T) : T))
{
import core.internal.traits : Unconst;
static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~
" to "~Unconst!T.stringof~" in dup.");
return _dup!(T, Unconst!T)(a);
}
///
@safe unittest
{
auto arr = [1, 2];
auto arr2 = arr.dup;
arr[0] = 0;
assert(arr == [0, 2]);
assert(arr2 == [1, 2]);
}
/// ditto
// const overload to support implicit conversion to immutable (unique result, see DIP29)
@property T[] dup(T)(const(T)[] a)
if (is(const(T) : T))
{
return _dup!(const(T), T)(a);
}
/// Provide the .idup array property.
@property immutable(T)[] idup(T)(T[] a)
{
static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~
" to immutable in idup.");
return _dup!(T, immutable(T))(a);
}
/// ditto
@property immutable(T)[] idup(T:void)(const(T)[] a)
{
return a.dup;
}
///
@safe unittest
{
char[] arr = ['a', 'b', 'c'];
string s = arr.idup;
arr[0] = '.';
assert(s == "abc");
}
private U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T))
{
if (__ctfe)
return _dupCtfe!(T, U)(a);
import core.stdc.string : memcpy;
auto arr = _d_newarrayU(typeid(T[]), a.length);
memcpy(arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length);
return *cast(U[]*) &arr;
}
private U[] _dupCtfe(T, U)(scope T[] a)
{
static if (is(T : void))
assert(0, "Cannot dup a void[] array at compile time.");
else
{
U[] res;
foreach (ref e; a)
res ~= e;
return res;
}
}
private U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T))
{
// note: copyEmplace is `@system` inside a `@trusted` block, so the __ctfe branch
// has the extra duty to infer _dup `@system` when the copy-constructor is `@system`.
if (__ctfe)
return _dupCtfe!(T, U)(a);
import core.lifetime: copyEmplace;
U[] res = () @trusted {
auto arr = cast(U*) _d_newarrayU(typeid(T[]), a.length);
size_t i;
scope (failure)
{
import core.internal.lifetime: emplaceInitializer;
// Initialize all remaining elements to not destruct garbage
foreach (j; i .. a.length)
emplaceInitializer(cast() arr[j]);
}
for (; i < a.length; i++)
{
copyEmplace(a.ptr[i], arr[i]);
}
return cast(U[])(arr[0..a.length]);
} ();
return res;
}
// https://issues.dlang.org/show_bug.cgi?id=22107
@safe unittest
{
static int i;
@safe struct S
{
this(this) { i++; }
}
void fun(scope S[] values...) @safe
{
values.dup;
}
}
// HACK: This is a lie. `_d_arraysetcapacity` is neither `nothrow` nor `pure`, but this lie is
// necessary for now to prevent breaking code.
private extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* arrptr) pure nothrow;
/**
(Property) Gets the current _capacity of a slice. The _capacity is the size
that the slice can grow to before the underlying array must be
reallocated or extended.
If an append must reallocate a slice with no possibility of extension, then
`0` is returned. This happens when the slice references a static array, or
if another slice references elements past the end of the current slice.
Note: The _capacity of a slice may be impacted by operations on other slices.
*/
@property size_t capacity(T)(T[] arr) pure nothrow @trusted
{
return _d_arraysetcapacity(typeid(T[]), 0, cast(void[]*)&arr);
}
///
@safe unittest
{
//Static array slice: no capacity
int[4] sarray = [1, 2, 3, 4];
int[] slice = sarray[];
assert(sarray.capacity == 0);
//Appending to slice will reallocate to a new array
slice ~= 5;
assert(slice.capacity >= 5);
//Dynamic array slices
int[] a = [1, 2, 3, 4];
int[] b = a[1 .. $];
int[] c = a[1 .. $ - 1];
debug(SENTINEL) {} else // non-zero capacity very much depends on the array and GC implementation
{
assert(a.capacity != 0);
assert(a.capacity == b.capacity + 1); //both a and b share the same tail
}
assert(c.capacity == 0); //an append to c must relocate c.
}
/**
Reserves capacity for a slice. The capacity is the size
that the slice can grow to before the underlying array must be
reallocated or extended.
Returns: The new capacity of the array (which may be larger than
the requested capacity).
*/
size_t reserve(T)(ref T[] arr, size_t newcapacity) pure nothrow @trusted
{
if (__ctfe)
return newcapacity;
else
return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void[]*)&arr);
}
///
@safe unittest
{
//Static array slice: no capacity. Reserve relocates.
int[4] sarray = [1, 2, 3, 4];
int[] slice = sarray[];
auto u = slice.reserve(8);
assert(u >= 8);
assert(&sarray[0] !is &slice[0]);
assert(slice.capacity == u);
//Dynamic array slices
int[] a = [1, 2, 3, 4];
a.reserve(8); //prepare a for appending 4 more items
auto p = &a[0];
u = a.capacity;
a ~= [5, 6, 7, 8];
assert(p == &a[0]); //a should not have been reallocated
assert(u == a.capacity); //a should not have been extended
}
// https://issues.dlang.org/show_bug.cgi?id=12330, reserve() at CTFE time
@safe unittest
{
int[] foo() {
int[] result;
auto a = result.reserve = 5;
assert(a == 5);
return result;
}
enum r = foo();
}
// Issue 6646: should be possible to use array.reserve from SafeD.
@safe unittest
{
int[] a;
a.reserve(10);
}
// HACK: This is a lie. `_d_arrayshrinkfit` is not `nothrow`, but this lie is necessary
// for now to prevent breaking code.
private extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow;
/**
Assume that it is safe to append to this array. Appends made to this array
after calling this function may append in place, even if the array was a
slice of a larger array to begin with.
Use this only when it is certain there are no elements in use beyond the
array in the memory block. If there are, those elements will be
overwritten by appending to this array.
Warning: Calling this function, and then using references to data located after the
given array results in undefined behavior.
Returns:
The input is returned.
*/
auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system
{
_d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr));
return arr;
}
///
@system unittest
{
int[] a = [1, 2, 3, 4];
// Without assumeSafeAppend. Appending relocates.
int[] b = a [0 .. 3];
b ~= 5;
assert(a.ptr != b.ptr);
debug(SENTINEL) {} else
{
// With assumeSafeAppend. Appending overwrites.
int[] c = a [0 .. 3];
c.assumeSafeAppend() ~= 5;
assert(a.ptr == c.ptr);
}
}
@system unittest
{
int[] arr;
auto newcap = arr.reserve(2000);
assert(newcap >= 2000);
assert(newcap == arr.capacity);
auto ptr = arr.ptr;
foreach (i; 0..2000)
arr ~= i;
assert(ptr == arr.ptr);
arr = arr[0..1];
arr.assumeSafeAppend();
arr ~= 5;
assert(ptr == arr.ptr);
}
@system unittest
{
int[] arr = [1, 2, 3];
void foo(ref int[] i)
{
i ~= 5;
}
arr = arr[0 .. 2];
foo(assumeSafeAppend(arr)); //pass by ref
assert(arr[]==[1, 2, 5]);
arr = arr[0 .. 1].assumeSafeAppend(); //pass by value
}
// https://issues.dlang.org/show_bug.cgi?id=10574
@system unittest
{
int[] a;
immutable(int[]) b;
auto a2 = &assumeSafeAppend(a);
auto b2 = &assumeSafeAppend(b);
auto a3 = assumeSafeAppend(a[]);
auto b3 = assumeSafeAppend(b[]);
assert(is(typeof(*a2) == int[]));
assert(is(typeof(*b2) == immutable(int[])));
assert(is(typeof(a3) == int[]));
assert(is(typeof(b3) == immutable(int[])));
}
private extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow;
private void _doPostblit(T)(T[] arr)
{
// infer static postblit type, run postblit if any
static if (__traits(hasPostblit, T))
{
static if (__traits(isStaticArray, T) && is(T : E[], E))
_doPostblit(cast(E[]) arr);
else static if (!is(typeof(arr[0].__xpostblit())) && is(immutable T == immutable U, U))
foreach (ref elem; (() @trusted => cast(U[]) arr)())
elem.__xpostblit();
else
foreach (ref elem; arr)
elem.__xpostblit();
}
}
@safe unittest
{
static struct S1 { int* p; }
static struct S2 { @disable this(); }
static struct S3 { @disable this(this); }
int dg1() pure nothrow @safe
{
{
char[] m;
string i;
m = m.dup;
i = i.idup;
m = i.dup;
i = m.idup;
}
{
S1[] m;
immutable(S1)[] i;
m = m.dup;
i = i.idup;
static assert(!is(typeof(m.idup)));
static assert(!is(typeof(i.dup)));
}
{
S3[] m;
immutable(S3)[] i;
static assert(!is(typeof(m.dup)));
static assert(!is(typeof(i.idup)));
}
{
shared(S1)[] m;
m = m.dup;
static assert(!is(typeof(m.idup)));
}
{
int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0);
}
return 1;
}
int dg2() pure nothrow @safe
{
{
S2[] m = [S2.init, S2.init];
immutable(S2)[] i = [S2.init, S2.init];
m = m.dup;
m = i.dup;
i = m.idup;
i = i.idup;
}
return 2;
}
enum a = dg1();
enum b = dg2();
assert(dg1() == a);
assert(dg2() == b);
}
@system unittest
{
static struct Sunpure { this(this) @safe nothrow {} }
static struct Sthrow { this(this) @safe pure {} }
static struct Sunsafe { this(this) @system pure nothrow {} }
static struct Snocopy { @disable this(this); }
[].dup!Sunpure;
[].dup!Sthrow;
cast(void) [].dup!Sunsafe;
static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
static assert(!__traits(compiles, () { [].dup!Snocopy; }));
[].idup!Sunpure;
[].idup!Sthrow;
[].idup!Sunsafe;
static assert(!__traits(compiles, () pure { [].idup!Sunpure; }));
static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; }));
static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; }));
static assert(!__traits(compiles, () { [].idup!Snocopy; }));
}
@safe unittest
{
// test that the copy-constructor is called with .dup
static struct ArrElem
{
int a;
this(int a)
{
this.a = a;
}
this(ref const ArrElem)
{
a = 2;
}
this(ref ArrElem) immutable
{
a = 3;
}
}
auto arr = [ArrElem(1), ArrElem(1)];
ArrElem[] b = arr.dup;
assert(b[0].a == 2 && b[1].a == 2);
immutable ArrElem[] c = arr.idup;
assert(c[0].a == 3 && c[1].a == 3);
}
@system unittest
{
static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} }
static struct Sthrow { this(ref const typeof(this)) @safe pure {} }
static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} }
[].dup!Sunpure;
[].dup!Sthrow;
cast(void) [].dup!Sunsafe;
static assert(!__traits(compiles, () pure { [].dup!Sunpure; }));
static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; }));
static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; }));
// for idup to work on structs that have copy constructors, it is necessary
// that the struct defines a copy constructor that creates immutable objects
static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} }
static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} }
static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} }
[].idup!ISunpure;
[].idup!ISthrow;
[].idup!ISunsafe;
static assert(!__traits(compiles, () pure { [].idup!ISunpure; }));
static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; }));
static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; }));
}
@safe unittest
{
static int*[] pureFoo() pure { return null; }
{ char[] s; immutable x = s.dup; }
{ immutable x = (cast(int*[])null).dup; }
{ immutable x = pureFoo(); }
{ immutable x = pureFoo().dup; }
}
@safe unittest
{
auto a = [1, 2, 3];
auto b = a.dup;
debug(SENTINEL) {} else
assert(b.capacity >= 3);
}
@system unittest
{
// Bugzilla 12580
void[] m = [0];
shared(void)[] s = [cast(shared)1];
immutable(void)[] i = [cast(immutable)2];
s = s.dup;
static assert(is(typeof(s.dup) == shared(void)[]));
m = i.dup;
i = m.dup;
i = i.idup;
i = m.idup;
i = s.idup;
i = s.dup;
static assert(!__traits(compiles, m = s.dup));
}
@safe unittest
{
// Bugzilla 13809
static struct S
{
this(this) {}
~this() {}
}
S[] arr;
auto a = arr.dup;
}
@system unittest
{
// Bugzilla 16504
static struct S
{
__gshared int* gp;
int* p;
// postblit and hence .dup could escape
this(this) { gp = p; }
}
int p;
scope S[1] arr = [S(&p)];
auto a = arr.dup; // dup does escape
}
// https://issues.dlang.org/show_bug.cgi?id=21983
// dup/idup destroys partially constructed arrays on failure
@safe unittest
{
static struct SImpl(bool postblit)
{
int num;
long l = 0xDEADBEEF;
static if (postblit)
{
this(this)
{
if (this.num == 3)
throw new Exception("");
}
}
else
{
this(scope ref const SImpl other)
{
if (other.num == 3)
throw new Exception("");
this.num = other.num;
this.l = other.l;
}
}
~this() @trusted
{
if (l != 0xDEADBEEF)
{
import core.stdc.stdio;
printf("Unexpected value: %lld\n", l);
fflush(stdout);
assert(false);
}
}
}
alias Postblit = SImpl!true;
alias Copy = SImpl!false;
static int test(S)()
{
S[4] arr = [ S(1), S(2), S(3), S(4) ];
try
{
arr.dup();
assert(false);
}
catch (Exception)
{
return 1;
}
}
static assert(test!Postblit());
assert(test!Postblit());
static assert(test!Copy());
assert(test!Copy());
}
/**
Destroys the given object and optionally resets to initial state. It's used to
_destroy an object, calling its destructor or finalizer so it no longer
references any other objects. It does $(I not) initiate a GC cycle or free
any GC memory.
If `initialize` is supplied `false`, the object is considered invalid after
destruction, and should not be referenced.
*/
void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct))
{
import core.internal.destruction : destructRecurse;
destructRecurse(obj);
static if (initialize)
{
import core.internal.lifetime : emplaceInitializer;
emplaceInitializer(obj); // emplace T.init
}
}
@safe unittest
{
struct A { string s = "A"; }
A a = {s: "B"};
assert(a.s == "B");
a.destroy;
assert(a.s == "A");
}
nothrow @safe @nogc unittest
{
{
struct A { string s = "A"; }
A a;
a.s = "asd";
destroy!false(a);
assert(a.s == "asd");
destroy(a);
assert(a.s == "A");
}
{
static int destroyed = 0;
struct C
{
string s = "C";
~this() nothrow @safe @nogc
{
destroyed ++;
}
}
struct B
{
C c;
string s = "B";
~this() nothrow @safe @nogc
{
destroyed ++;
}
}
B a;
a.s = "asd";
a.c.s = "jkl";
destroy!false(a);
assert(destroyed == 2);
assert(a.s == "asd");
assert(a.c.s == "jkl" );
destroy(a);
assert(destroyed == 4);
assert(a.s == "B");
assert(a.c.s == "C" );
}
}
private extern (C) void rt_finalize(void *data, bool det=true) nothrow;
/// ditto
void destroy(bool initialize = true, T)(T obj) if (is(T == class))
{
static if (__traits(getLinkage, T) == "C++")
{
static if (__traits(hasMember, T, "__xdtor"))
obj.__xdtor();
static if (initialize)
{
const initializer = __traits(initSymbol, T);
(cast(void*)obj)[0 .. initializer.length] = initializer[];
}
}
else
rt_finalize(cast(void*)obj);
}
/// ditto
void destroy(bool initialize = true, T)(T obj) if (is(T == interface))
{
static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface");
destroy!initialize(cast(Object)obj);
}
/// Reference type demonstration
@system unittest
{
class C
{
struct Agg
{
static int dtorCount;
int x = 10;
~this() { dtorCount++; }
}
static int dtorCount;
string s = "S";
Agg a;
~this() { dtorCount++; }
}
C c = new C();
assert(c.dtorCount == 0); // destructor not yet called
assert(c.s == "S"); // initial state `c.s` is `"S"`
assert(c.a.dtorCount == 0); // destructor not yet called
assert(c.a.x == 10); // initial state `c.a.x` is `10`
c.s = "T";
c.a.x = 30;
assert(c.s == "T"); // `c.s` is `"T"`
destroy(c);
assert(c.dtorCount == 1); // `c`'s destructor was called
assert(c.s == "S"); // `c.s` is back to its inital state, `"S"`
assert(c.a.dtorCount == 1); // `c.a`'s destructor was called
assert(c.a.x == 10); // `c.a.x` is back to its inital state, `10`
// check C++ classes work too!
extern (C++) class CPP
{
struct Agg
{
__gshared int dtorCount;
int x = 10;
~this() { dtorCount++; }
}
__gshared int dtorCount;
string s = "S";
Agg a;
~this() { dtorCount++; }
}
CPP cpp = new CPP();
assert(cpp.dtorCount == 0); // destructor not yet called
assert(cpp.s == "S"); // initial state `cpp.s` is `"S"`
assert(cpp.a.dtorCount == 0); // destructor not yet called
assert(cpp.a.x == 10); // initial state `cpp.a.x` is `10`
cpp.s = "T";
cpp.a.x = 30;
assert(cpp.s == "T"); // `cpp.s` is `"T"`
destroy!false(cpp); // destroy without initialization
assert(cpp.dtorCount == 1); // `cpp`'s destructor was called
assert(cpp.s == "T"); // `cpp.s` is not initialized
assert(cpp.a.dtorCount == 1); // `cpp.a`'s destructor was called
assert(cpp.a.x == 30); // `cpp.a.x` is not initialized
destroy(cpp);
assert(cpp.dtorCount == 2); // `cpp`'s destructor was called again
assert(cpp.s == "S"); // `cpp.s` is back to its inital state, `"S"`
assert(cpp.a.dtorCount == 2); // `cpp.a`'s destructor was called again
assert(cpp.a.x == 10); // `cpp.a.x` is back to its inital state, `10`
}
/// Value type demonstration
@safe unittest
{
int i;
assert(i == 0); // `i`'s initial state is `0`
i = 1;
assert(i == 1); // `i` changed to `1`
destroy!false(i);
assert(i == 1); // `i` was not initialized
destroy(i);
assert(i == 0); // `i` is back to its initial state `0`
}
@system unittest
{
extern(C++)
static class C
{
void* ptr;
this() {}
}
destroy!false(new C());
destroy!true(new C());
}
@system unittest
{
// class with an `alias this`
class A
{
static int dtorCount;
~this()
{
dtorCount++;
}
}
class B
{
A a;
alias a this;
this()
{
a = new A;
}
static int dtorCount;
~this()
{
dtorCount++;
}
}
auto b = new B;
assert(A.dtorCount == 0);
assert(B.dtorCount == 0);
destroy(b);
assert(A.dtorCount == 0);
assert(B.dtorCount == 1);
auto a = new A;
destroy(a);
assert(A.dtorCount == 1);
}
@system unittest
{
interface I { }
{
class A: I { string s = "A"; this() {} }
auto a = new A, b = new A;
a.s = b.s = "asd";
destroy(a);
assert(a.s == "A");
I i = b;
destroy(i);
assert(b.s == "A");
}
{
static bool destroyed = false;
class B: I
{
string s = "B";
this() {}
~this()
{
destroyed = true;
}
}
auto a = new B, b = new B;
a.s = b.s = "asd";
destroy(a);
assert(destroyed);
assert(a.s == "B");
destroyed = false;
I i = b;
destroy(i);
assert(destroyed);
assert(b.s == "B");
}
// this test is invalid now that the default ctor is not run after clearing
version (none)
{
class C
{
string s;
this()
{
s = "C";
}
}
auto a = new C;
a.s = "asd";
destroy(a);
assert(a.s == "C");
}
}
nothrow @safe @nogc unittest
{
{
struct A { string s = "A"; }
A a;
a.s = "asd";
destroy!false(a);
assert(a.s == "asd");
destroy(a);
assert(a.s == "A");
}
{
static int destroyed = 0;
struct C
{
string s = "C";
~this() nothrow @safe @nogc
{
destroyed ++;
}
}
struct B
{
C c;
string s = "B";
~this() nothrow @safe @nogc
{
destroyed ++;
}
}
B a;
a.s = "asd";
a.c.s = "jkl";
destroy!false(a);
assert(destroyed == 2);
assert(a.s == "asd");
assert(a.c.s == "jkl" );
destroy(a);
assert(destroyed == 4);
assert(a.s == "B");
assert(a.c.s == "C" );
}
}
nothrow unittest
{
// Bugzilla 20049: Test to ensure proper behavior of `nothrow` destructors
class C
{
static int dtorCount = 0;
this() nothrow {}
~this() nothrow { dtorCount++; }
}
auto c = new C;
destroy(c);
assert(C.dtorCount == 1);
}
/// ditto
void destroy(bool initialize = true, T)(ref T obj)
if (__traits(isStaticArray, T))
{
foreach_reverse (ref e; obj[])
destroy!initialize(e);
}
@safe unittest
{
int[2] a;
a[0] = 1;
a[1] = 2;
destroy!false(a);
assert(a == [ 1, 2 ]);
destroy(a);
assert(a == [ 0, 0 ]);
}
@safe unittest
{
static struct vec2f {
float[2] values;
alias values this;
}
vec2f v;
destroy!(true, vec2f)(v);
}
@system unittest
{
// Bugzilla 15009
static string op;
static struct S
{
int x;
this(int x) { op ~= "C" ~ cast(char)('0'+x); this.x = x; }
this(this) { op ~= "P" ~ cast(char)('0'+x); }
~this() { op ~= "D" ~ cast(char)('0'+x); }
}
{
S[2] a1 = [S(1), S(2)];
op = "";
}
assert(op == "D2D1"); // built-in scope destruction
{
S[2] a1 = [S(1), S(2)];
op = "";
destroy(a1);
assert(op == "D2D1"); // consistent with built-in behavior
}
{
S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]];
op = "";
}
assert(op == "D4D3D2D1");
{
S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]];
op = "";
destroy(a2);
assert(op == "D4D3D2D1", op);
}
}
// https://issues.dlang.org/show_bug.cgi?id=19218
@system unittest
{
static struct S
{
static dtorCount = 0;
~this() { ++dtorCount; }
}
static interface I
{
ref S[3] getArray();
alias getArray this;
}
static class C : I
{
static dtorCount = 0;
~this() { ++dtorCount; }
S[3] a;
alias a this;
ref S[3] getArray() { return a; }
}
C c = new C();
destroy(c);
assert(S.dtorCount == 3);
assert(C.dtorCount == 1);
I i = new C();
destroy(i);
assert(S.dtorCount == 6);
assert(C.dtorCount == 2);
}
/// ditto
void destroy(bool initialize = true, T)(ref T obj)
if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T))
{
static if (initialize)
obj = T.init;
}
@safe unittest
{
{
int a = 42;
destroy!false(a);
assert(a == 42);
destroy(a);
assert(a == 0);
}
{
float a = 42;
destroy!false(a);
assert(a == 42);
destroy(a);
assert(a != a); // isnan
}
}
@safe unittest
{
// Bugzilla 14746
static struct HasDtor
{
~this() { assert(0); }
}
static struct Owner
{
HasDtor* ptr;
alias ptr this;
}
Owner o;
assert(o.ptr is null);
destroy(o); // must not reach in HasDtor.__dtor()
}
/* ************************************************************************
COMPILER SUPPORT
The compiler lowers certain expressions to instantiations of the following
templates. They must be implicitly imported, which is why they are here
in this file. They must also be `public` as they must be visible from the
scope in which they are instantiated. They are explicitly undocumented as
they are only intended to be instantiated by the compiler, not the user.
**************************************************************************/
public import core.internal.entrypoint : _d_cmain;
public import core.internal.array.appending : _d_arrayappendTImpl;
public import core.internal.array.appending : _d_arrayappendcTXImpl;
public import core.internal.array.comparison : __cmp;
public import core.internal.array.equality : __equals;
public import core.internal.array.casting: __ArrayCast;
public import core.internal.array.concatenation : _d_arraycatnTXImpl;
public import core.internal.array.construction : _d_arrayctor;
public import core.internal.array.construction : _d_arraysetctor;
public import core.internal.array.capacity: _d_arraysetlengthTImpl;
public import core.lifetime : _d_delstructImpl;
public import core.internal.dassert: _d_assert_fail;
public import core.internal.destruction: __ArrayDtor;
public import core.internal.moving: __move_post_blt;
public import core.internal.postblit: __ArrayPostblit;
public import core.internal.switch_: __switch;
public import core.internal.switch_: __switch_error;
public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable);
// Compare class and interface objects for ordering.
int __cmp(C1, C2)(C1 lhs, C2 rhs)
if ((is(C1 : const(Object)) || (is(C1 == interface) && (__traits(getLinkage, C1) == "D"))) &&
(is(C2 : const(Object)) || (is(C2 == interface) && (__traits(getLinkage, C2) == "D"))))
{
static if (is(C1 == typeof(null)) && is(C2 == typeof(null)))
{
return 0;
}
else static if (is(C1 == typeof(null)))
{
// Regard null references as always being "less than"
return -1;
}
else static if (is(C2 == typeof(null)))
{
return 1;
}
else
{
if (lhs is rhs)
return 0;
if (lhs is null)
return -1;
if (rhs is null)
return 1;
return lhs.opCmp(rhs);
}
}
// objects
@safe unittest
{
class C
{
int i;
this(int i) { this.i = i; }
override int opCmp(Object c) const @safe
{
return i - (cast(C)c).i;
}
}
auto c1 = new C(1);
auto c2 = new C(2);
assert(__cmp(c1, null) > 0);
assert(__cmp(null, c1) < 0);
assert(__cmp(c1, c1) == 0);
assert(__cmp(c1, c2) < 0);
assert(__cmp(c2, c1) > 0);
assert(__cmp([c1, c1][], [c2, c2][]) < 0);
assert(__cmp([c2, c2], [c1, c1]) > 0);
}
// structs
@safe unittest
{
struct C
{
ubyte i;
this(ubyte i) { this.i = i; }
}
auto c1 = C(1);
auto c2 = C(2);
assert(__cmp([c1, c1][], [c2, c2][]) < 0);
assert(__cmp([c2, c2], [c1, c1]) > 0);
assert(__cmp([c2, c2], [c2, c1]) > 0);
}
@safe unittest
{
auto a = "hello"c;
assert(a > "hel");
assert(a >= "hel");
assert(a < "helloo");
assert(a <= "helloo");
assert(a > "betty");
assert(a >= "betty");
assert(a == "hello");
assert(a <= "hello");
assert(a >= "hello");
assert(a < "я");
}
// Used in Exception Handling LSDA tables to 'wrap' C++ type info
// so it can be distinguished from D TypeInfo
class __cpp_type_info_ptr
{
void* ptr; // opaque pointer to C++ RTTI type info
}
// Compiler hook into the runtime implementation of array (vector) operations.
template _arrayOp(Args...)
{
import core.internal.array.operations;
alias _arrayOp = arrayOp!Args;
}
public import core.builtins : __ctfeWrite;
/**
Provides an "inline import", i.e. an `import` that is only available for a
limited lookup. For example:
---
void fun(imported!"std.stdio".File input)
{
... use File from std.stdio normally ...
}
---
There is no need to import `std.stdio` at top level, so `fun` carries its own
dependencies. The same approach can be used for template constraints:
---
void fun(T)(imported!"std.stdio".File input, T value)
if (imported!"std.traits".isIntegral!T)
{
...
}
---
An inline import may be used in conjunction with the `with` statement as well.
Inside the scope controlled by `with`, all symbols in the imported module are
made available:
---
void fun()
{
with (imported!"std.datetime")
with (imported!"std.stdio")
{
Clock.currTime.writeln;
}
}
---
The advantages of inline imports over top-level uses of the `import` declaration
are the following:
$(UL
$(LI The `imported` template specifies dependencies at declaration level, not at
module level. This allows reasoning about the dependency cost of declarations in
separation instead of aggregated at module level.)
$(LI Declarations using `imported` are easier to move around because they don't
require top-level context, making for simpler and quicker refactorings.)
$(LI Declarations using `imported` scale better with templates. This is because
templates that are not instantiated do not have their parameters and constraints
instantiated, so additional modules are not imported without necessity. This
makes the cost of unused templates negligible. Dependencies are pulled on a need
basis depending on the declarations used by client code.)
)
The use of `imported` also has drawbacks:
$(UL
$(LI If most declarations in a module need the same imports, then factoring them
at top level, outside the declarations, is simpler than repeating them.)
$(LI Traditional dependency-tracking tools such as make and other build systems
assume file-level dependencies and need special tooling (such as rdmd) in order
to work efficiently.)
$(LI Dependencies at the top of a module are easier to inspect quickly than
dependencies spread throughout the module.)
)
See_Also: The $(HTTP forum.dlang.org/post/tzqzmqhankrkbrfsrmbo@forum.dlang.org,
forum discussion) that led to the creation of the `imported` facility. Credit is
due to Daniel Nielsen and Dominikus Dittes Scherkl.
*/
template imported(string moduleName)
{
mixin("import imported = " ~ moduleName ~ ";");
}