Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 98e51b1

Browse files
committed
Add __delete for migration from delete
1 parent 97d25f2 commit 98e51b1

2 files changed

Lines changed: 268 additions & 0 deletions

File tree

changelog/core-memory-__delete.dd

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
`core.memory.__delete` has been added
2+
3+
$(REF __delete, core, memory) allows easy migration from the deprecated `delete`.
4+
`__delete` behaves exactly like `delete`:
5+
6+
---
7+
bool dtorCalled;
8+
class B
9+
{
10+
int test;
11+
~this()
12+
{
13+
dtorCalled = true;
14+
}
15+
}
16+
B b = new B();
17+
B a = b;
18+
b.test = 10;
19+
20+
__delete(b);
21+
assert(b is null);
22+
assert(dtorCalled);
23+
// but be careful, a still points to it
24+
assert(a !is null);
25+
---
26+
27+
For example, on a Posix platform you can simply run:
28+
29+
$(CONSOLE
30+
sed "s/delete \(.*\);/__delete(\1);/" -i **/*.d
31+
)
32+
33+
Users should prefer $(REF destroy, object)` to explicitly finalize objects,
34+
and only resort to $(REF __delete, core,memory) when $(REF destroy, object)
35+
would not be a feasible option.

src/core/memory.d

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,3 +919,236 @@ extern (C) private pure @system @nogc nothrow
919919

920920
pragma(mangle, "free") void fakePureFree(void* ptr);
921921
}
922+
923+
extern(C) private @system nothrow @nogc
924+
{
925+
pragma(mangle, "_d_delinterface") void _d_delinterface(void**);
926+
pragma(mangle, "_d_delclass") void _d_delclass(Object*);
927+
pragma(mangle, "_d_delstruct") void _d_delstruct(void**, TypeInfo_Struct);
928+
pragma(mangle, "_d_delmemory") void _d_delmemory(void**);
929+
pragma(mangle, "_d_delarray_t") void _d_delarray_t(void**, TypeInfo_Struct);
930+
}
931+
932+
/**
933+
Destroys and then deallocates an object.
934+
935+
In detail, `__delete(x)` returns with no effect if `x` is `null`. Otherwise, it
936+
performs the following actions in sequence:
937+
$(UL
938+
$(LI
939+
Calls the destructor `~this()` for the object referred to by `x`
940+
(if `x` is a class or interface reference) or
941+
for the object pointed to by `x` (if `x` is a pointer to a `struct`).
942+
Arrays of structs call the destructor, if defined, for each element in the array.
943+
If no destructor is defined, this step has no effect.
944+
)
945+
$(LI
946+
Frees the memory allocated for `x`. If `x` is a reference to a class
947+
or interface, the memory allocated for the underlying instance is freed. If `x` is
948+
a pointer, the memory allocated for the pointed-to object is freed. If `x` is a
949+
built-in array, the memory allocated for the array is freed.
950+
If `x` does not refer to memory previously allocated with `new` (or the lower-level
951+
equivalents in the GC API), the behavior is undefined.
952+
)
953+
$(LI
954+
Lastly, `x` is set to `null`. Any attempt to read or write the freed memory via
955+
other references will result in undefined behavior.
956+
)
957+
)
958+
959+
Note: Users should prefer $(REF destroy, object)` to explicitly finalize objects,
960+
and only resort to $(REF __delete, core,memory) when $(REF destroy, object)
961+
wouldn't be a feasible option.
962+
963+
Params:
964+
x = aggregate object that should be destroyed
965+
966+
See_Also: $(REF destroy, object), $(REF free, core,GC)
967+
968+
History:
969+
970+
The `delete` keyword allowed to free GC-allocated memory.
971+
As this is inherently not `@safe`, it has been deprecated.
972+
This function has been added to provide an easy transition from `delete`.
973+
It performs the same functionality as the former `delete` keyword.
974+
*/
975+
void __delete(T)(ref T x) @system
976+
{
977+
static void _destructRecurse(S)(ref S s)
978+
if (is(S == struct))
979+
{
980+
static if (__traits(hasMember, S, "__xdtor") &&
981+
// Bugzilla 14746: Check that it's the exact member of S.
982+
__traits(isSame, S, __traits(parent, s.__xdtor)))
983+
s.__xdtor();
984+
}
985+
986+
// See also: https://github.com/dlang/dmd/blob/v2.078.0/src/dmd/e2ir.d#L3886
987+
static if (is(T == interface))
988+
{
989+
.object.destroy(x);
990+
}
991+
else static if (is(T == class))
992+
{
993+
.object.destroy(x);
994+
}
995+
else static if (is(T == U*, U))
996+
{
997+
static if (is(U == struct))
998+
_destructRecurse(*x);
999+
}
1000+
else static if (is(T : E[], E))
1001+
{
1002+
static if (is(E == struct))
1003+
{
1004+
foreach (ref e; x)
1005+
_destructRecurse(e);
1006+
}
1007+
}
1008+
else
1009+
{
1010+
static assert(0, "It is not possible to delete: `" ~ T.stringof ~ "`");
1011+
}
1012+
1013+
static if (is(T == interface) || is(T == class))
1014+
{
1015+
GC.free(cast(void*) x);
1016+
x = null;
1017+
}
1018+
else static if (is(T == U2*, U2) || is(T : E2[], E2))
1019+
{
1020+
GC.free(&x);
1021+
x = null;
1022+
}
1023+
}
1024+
1025+
/// Deleting classes
1026+
unittest
1027+
{
1028+
bool dtorCalled;
1029+
class B
1030+
{
1031+
int test;
1032+
~this()
1033+
{
1034+
dtorCalled = true;
1035+
}
1036+
}
1037+
B b = new B();
1038+
B a = b;
1039+
b.test = 10;
1040+
1041+
assert(GC.addrOf(cast(void*) b) != null);
1042+
__delete(b);
1043+
assert(b is null);
1044+
assert(dtorCalled);
1045+
assert(GC.addrOf(cast(void*) b) == null);
1046+
// but be careful, a still points to it
1047+
assert(a !is null);
1048+
assert(GC.addrOf(cast(void*) a) !is null);
1049+
}
1050+
1051+
/// Deleting interfaces
1052+
unittest
1053+
{
1054+
bool dtorCalled;
1055+
interface A
1056+
{
1057+
int quack();
1058+
}
1059+
class B : A
1060+
{
1061+
int a;
1062+
int quack()
1063+
{
1064+
a++;
1065+
return a;
1066+
}
1067+
~this()
1068+
{
1069+
dtorCalled = true;
1070+
}
1071+
}
1072+
A a = new B();
1073+
a.quack();
1074+
1075+
assert(GC.addrOf(cast(void*) a) != null);
1076+
__delete(a);
1077+
assert(a is null);
1078+
assert(dtorCalled);
1079+
assert(GC.addrOf(cast(void*) a) == null);
1080+
}
1081+
1082+
/// Deleting structs
1083+
unittest
1084+
{
1085+
bool dtorCalled;
1086+
struct A
1087+
{
1088+
string test;
1089+
~this()
1090+
{
1091+
dtorCalled = true;
1092+
}
1093+
}
1094+
auto a = new A("foo");
1095+
1096+
assert(GC.addrOf(cast(void*) a) != null);
1097+
__delete(a);
1098+
assert(a is null);
1099+
assert(dtorCalled);
1100+
assert(GC.addrOf(cast(void*) a) == null);
1101+
}
1102+
1103+
/// Deleting arrays
1104+
unittest
1105+
{
1106+
int[] a = [1, 2, 3];
1107+
auto b = a;
1108+
1109+
assert(GC.addrOf(b.ptr) != null);
1110+
__delete(b);
1111+
assert(b is null);
1112+
assert(GC.addrOf(b.ptr) == null);
1113+
// but be careful, a still points to it
1114+
assert(a !is null);
1115+
assert(GC.addrOf(a.ptr) !is null);
1116+
}
1117+
1118+
/// Deleting arrays of structs
1119+
unittest
1120+
{
1121+
int dtorCalled;
1122+
struct A
1123+
{
1124+
int a;
1125+
~this()
1126+
{
1127+
dtorCalled++;
1128+
}
1129+
}
1130+
auto arr = [A(1), A(2), A(3)];
1131+
1132+
assert(GC.addrOf(arr.ptr) != null);
1133+
__delete(arr);
1134+
assert(dtorCalled == 3);
1135+
assert(GC.addrOf(arr.ptr) == null);
1136+
}
1137+
1138+
// Deleting raw memory
1139+
unittest
1140+
{
1141+
import core.memory : GC;
1142+
auto a = GC.malloc(5);
1143+
assert(GC.addrOf(cast(void*) a) != null);
1144+
__delete(a);
1145+
assert(a is null);
1146+
assert(GC.addrOf(cast(void*) a) == null);
1147+
}
1148+
1149+
// __delete returns with no effect if x is null
1150+
unittest
1151+
{
1152+
Object x = null;
1153+
__delete(x);
1154+
}

0 commit comments

Comments
 (0)