Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions compiler/src/dmd/e2ir.d
Original file line number Diff line number Diff line change
Expand Up @@ -1083,10 +1083,7 @@ elem* toElem(Expression e, IRState *irs)
assert(!(global.params.ehnogc && ne.thrownew),
"This should have been rewritten to `_d_newThrowable` in the semantic phase.");

Symbol *csym = toSymbol(cd);
const rtl = RTLSYM.NEWCLASS;
ex = el_bin(OPcall,TYnptr,el_var(getRtlsym(rtl)),el_ptr(csym));
toTraceGC(irs, ex, ne.loc);
ex = toElem(ne.lowering, irs);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are previous lowerings going to be retroactively transplanted into a lowering field?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's the plan.

ectype = null;

if (cd.isNested())
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -1544,7 +1544,8 @@ extern (C++) abstract class Expression : ASTNode
// Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)),
// so don't print anything to avoid double error messages.
if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT
|| f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX))
|| f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX
|| f.ident == Id._d_newclassT))
error("`@nogc` %s `%s` cannot call non-@nogc %s `%s`",
sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars());

Expand Down Expand Up @@ -3650,6 +3651,8 @@ extern (C++) final class NewExp : Expression
bool onstack; // allocate on stack
bool thrownew; // this NewExp is the expression of a ThrowStatement

Expression lowering; // lowered druntime hook: `_d_newclass`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? If yes keep it but keep an eye on adding unnecessary fields as dmd's classes are absolutely enormous

Copy link
Member Author

@teodutu teodutu Jan 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's necessary. I need an expression where to store the lowering, so I can then pass it to the backend in e2ir.d. This expression can either be a field in NewExp or the result set by visit(NewExp) (instead of the original expression) in expressionsemantic.d. The latter option is difficult because it requires me to initialise the context pointer in the semantic phase. I tried doing this in the past and it's ugly. I'd rather use the existing machinery that handles context pointers in e2ir.d instead. Furthermore, I received feedback from @ibuclaw in the past that eliding the original expressions might hinder some optimisations in GDC and LDC.


extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments)
{
super(loc, EXP.new_, __traits(classInstanceSize, NewExp));
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ class NewExp final : public Expression
bool onstack; // allocate on stack
bool thrownew; // this NewExp is the expression of a ThrowStatement

Expression lowering; // lowered druntime hook: `_d_newclass`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't have this been a pointer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Fixed by #14872.


static NewExp *create(const Loc &loc, Expression *thisexp, Type *newtype, Expressions *arguments);
NewExp *syntaxCopy() override;

Expand Down
26 changes: 26 additions & 0 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -3781,6 +3781,32 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = id.expressionSemantic(sc);
return;
}
else if (!exp.onstack && !exp.type.isscope())
{
auto hook = global.params.tracegc ? Id._d_newclassTTrace : Id._d_newclassT;
if (!verifyHookExist(exp.loc, *sc, hook, "new class"))
return setError();

Expression id = new IdentifierExp(exp.loc, Id.empty);
id = new DotIdExp(exp.loc, id, Id.object);

auto tiargs = new Objects();
auto t = exp.newtype.unqualify(MODFlags.wild); // remove `inout`
tiargs.push(t);
id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs);
auto arguments = new Expressions();
if (global.params.tracegc)
{
auto funcname = (sc.callsc && sc.callsc.func) ?
sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars();
arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString()));
arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32));
arguments.push(new StringExp(exp.loc, funcname.toDString()));
}
id = new CallExp(exp.loc, id, arguments);

exp.lowering = id.expressionSemantic(sc);
}
}
else if (auto ts = tb.isTypeStruct())
{
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -7183,6 +7183,7 @@ class NewExp final : public Expression
CtorDeclaration* member;
bool onstack;
bool thrownew;
Expression* lowering;
static NewExp* create(const Loc& loc, Expression* thisexp, Type* newtype, Array<Expression* >* arguments);
NewExp* syntaxCopy() override;
void accept(Visitor* v) override;
Expand Down Expand Up @@ -8573,6 +8574,8 @@ struct Id final
static Identifier* criticalexit;
static Identifier* _d_delThrowable;
static Identifier* _d_newThrowable;
static Identifier* _d_newclassT;
static Identifier* _d_newclassTTrace;
static Identifier* _d_assert_fail;
static Identifier* dup;
static Identifier* _aaApply;
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/id.d
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,8 @@ immutable Msgtable[] msgtable =
{ "__ArrayDtor" },
{ "_d_delThrowable" },
{ "_d_newThrowable" },
{ "_d_newclassT" },
{ "_d_newclassTTrace" },
{ "_d_assert_fail" },
{ "dup" },
{ "_aaApply" },
Expand Down
7 changes: 7 additions & 0 deletions compiler/test/compilable/test23431_minimal.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ class Exception : Throwable

class Error { }

// Needed to lower `new Exception("ice")` to it.
T _d_newclassT(T)()
if (is(T == class))
{
return null;
}

void test23431()
{
int a;
Expand Down
7 changes: 7 additions & 0 deletions compiler/test/compilable/test23433.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ module object;
class Throwable { }
class Exception : Throwable { this(immutable(char)[]) { } }

// Needed to lower `new Exception("ice")` to it.
T _d_newclassT(T)()
if (is(T == class))
{
return null;
}

void test23433()
{
try
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/fail_compilation/fail308.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class MinHeap(NodeType)
unittest
{
struct TestType {}
MinHeap!(TestType) foo = new MinHeap!(TestType)();
MinHeap!(TestType) foo;
}
}
8 changes: 7 additions & 1 deletion compiler/test/fail_compilation/ice23569.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@
/*
TEST_OUTPUT:
---
fail_compilation/ice23569.d(18): Error: cannot compare classes for equality because `object.Object` was not declared
fail_compilation/ice23569.d(24): Error: cannot compare classes for equality because `object.Object` was not declared
---
*/
module object;

T _d_newclassT(T)()
if (is(T == class))
{
return null;
}

@safe unittest1()
{
class F
Expand Down
105 changes: 105 additions & 0 deletions druntime/src/core/lifetime.d
Original file line number Diff line number Diff line change
Expand Up @@ -2709,3 +2709,108 @@ T _d_newThrowable(T)() @trusted
assert(exc.refcount() == 1);
assert(e.refcount() == 1);
}

/**
* Create a new class instance.
* Allocates memory and sets fields to their initial value, but does not call a
* constructor.
* ---
* new C() // _d_newclass!(C)()
* ---
* Returns: newly created object
*/
T _d_newclassT(T)() @trusted
if (is(T == class))
{
import core.internal.traits : hasIndirections;
import core.exception : onOutOfMemoryError;
import core.memory : GC, pureMalloc;

alias BlkAttr = GC.BlkAttr;

auto init = __traits(initSymbol, T);
void* p;

static if (__traits(getLinkage, T) == "Windows")
{
p = pureMalloc(init.length);
if (!p)
onOutOfMemoryError();
}
else
{
BlkAttr attr = BlkAttr.NONE;

/* `extern(C++)`` classes don't have a classinfo pointer in their vtable,
* so the GC can't finalize them.
*/
static if (__traits(hasMember, T, "__dtor") && __traits(getLinkage, T) != "C++")
attr |= BlkAttr.FINALIZE;
static if (!hasIndirections!T)
attr |= BlkAttr.NO_SCAN;

p = GC.malloc(init.length, attr, typeid(T));
debug(PRINTF) printf(" p = %p\n", p);
}

debug(PRINTF)
{
printf("p = %p\n", p);
printf("init.ptr = %p, len = %llu\n", init.ptr, cast(ulong)init.length);
printf("vptr = %p\n", *cast(void**) init);
printf("vtbl[0] = %p\n", (*cast(void***) init)[0]);
printf("vtbl[1] = %p\n", (*cast(void***) init)[1]);
printf("init[0] = %x\n", (cast(uint*) init)[0]);
printf("init[1] = %x\n", (cast(uint*) init)[1]);
printf("init[2] = %x\n", (cast(uint*) init)[2]);
printf("init[3] = %x\n", (cast(uint*) init)[3]);
printf("init[4] = %x\n", (cast(uint*) init)[4]);
}

// initialize it
p[0 .. init.length] = init[];

debug(PRINTF) printf("initialization done\n");
return cast(T) p;
}

// Test allocation
@safe unittest
{
class C { }
C c = _d_newclassT!C();

assert(c !is null);
}

// Test initializers
@safe unittest
{
{
class C { int x, y; }
C c = _d_newclassT!C();

assert(c.x == 0);
assert(c.y == 0);
}
{
class C { int x = 2, y = 3; }
C c = _d_newclassT!C();

assert(c.x == 2);
assert(c.y == 3);
}
}

T _d_newclassTTrace(T)(string file, int line, string funcname) @trusted
{
version (D_TypeInfo)
{
import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
mixin(TraceHook!(T.stringof, "_d_newclassT"));

return _d_newclassT!T();
}
else
assert(0, "Cannot create new class if compiling without support for runtime type information!");
}
2 changes: 2 additions & 0 deletions druntime/src/object.d
Original file line number Diff line number Diff line change
Expand Up @@ -4648,6 +4648,8 @@ public import core.internal.switch_: __switch_error;

public import core.lifetime : _d_delstructImpl;
public import core.lifetime : _d_newThrowable;
public import core.lifetime : _d_newclassT;
public import core.lifetime : _d_newclassTTrace;

public @trusted @nogc nothrow pure extern (C) void _d_delThrowable(scope Throwable);

Expand Down
1 change: 0 additions & 1 deletion druntime/src/rt/tracegc.d
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ module rt.tracegc;

// version = tracegc;

extern (C) Object _d_newclass(const ClassInfo ci);
extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length);
extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length);
extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length);
Expand Down
3 changes: 2 additions & 1 deletion druntime/test/profile/myprofilegc.log.freebsd.32.exp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bytes allocated, allocations, type, function, file:line
48 1 float[] D main src/profilegc.d:42
48 1 int[] D main src/profilegc.d:41
32 1 void[] profilegc.main src/profilegc.d:55
16 1 C D main src/profilegc.d:12
16 1 char[] D main src/profilegc.d:34
16 1 char[] D main src/profilegc.d:36
16 1 closure profilegc.main.foo src/profilegc.d:45
Expand All @@ -15,5 +16,5 @@ bytes allocated, allocations, type, function, file:line
16 1 int[] D main src/profilegc.d:14
16 1 int[] D main src/profilegc.d:22
16 1 int[] D main src/profilegc.d:37
16 1 profilegc.main.C D main src/profilegc.d:12
16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2752
16 1 wchar[] D main src/profilegc.d:35
3 changes: 2 additions & 1 deletion druntime/test/profile/myprofilegc.log.freebsd.64.exp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ bytes allocated, allocations, type, function, file:line
64 1 double[] profilegc.main src/profilegc.d:56
48 1 float[] D main src/profilegc.d:42
48 1 int[] D main src/profilegc.d:41
32 1 profilegc.main.C D main src/profilegc.d:12
32 1 C D main src/profilegc.d:12
32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2752
32 1 void[] profilegc.main src/profilegc.d:55
16 1 char[] D main src/profilegc.d:34
16 1 char[] D main src/profilegc.d:36
Expand Down
3 changes: 2 additions & 1 deletion druntime/test/profile/myprofilegc.log.linux.32.exp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bytes allocated, allocations, type, function, file:line
48 1 float[] D main src/profilegc.d:42
48 1 int[] D main src/profilegc.d:41
32 1 void[] profilegc.main src/profilegc.d:55
16 1 C D main src/profilegc.d:12
16 1 char[] D main src/profilegc.d:34
16 1 char[] D main src/profilegc.d:36
16 1 closure profilegc.main.foo src/profilegc.d:45
Expand All @@ -15,5 +16,5 @@ bytes allocated, allocations, type, function, file:line
16 1 int[] D main src/profilegc.d:14
16 1 int[] D main src/profilegc.d:22
16 1 int[] D main src/profilegc.d:37
16 1 profilegc.main.C D main src/profilegc.d:12
16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2752
16 1 wchar[] D main src/profilegc.d:35
3 changes: 2 additions & 1 deletion druntime/test/profile/myprofilegc.log.linux.64.exp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ bytes allocated, allocations, type, function, file:line
64 1 double[] profilegc.main src/profilegc.d:56
48 1 float[] D main src/profilegc.d:42
48 1 int[] D main src/profilegc.d:41
32 1 profilegc.main.C D main src/profilegc.d:12
32 1 C D main src/profilegc.d:12
32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2752
32 1 void[] profilegc.main src/profilegc.d:55
16 1 char[] D main src/profilegc.d:34
16 1 char[] D main src/profilegc.d:36
Expand Down
3 changes: 2 additions & 1 deletion druntime/test/profile/myprofilegc.log.osx.32.exp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bytes allocated, allocations, type, function, file:line
64 1 int[] D main src/profilegc.d:41
64 1 double[] profilegc.main src/profilegc.d:56
32 1 void[] profilegc.main src/profilegc.d:55
16 1 C D main src/profilegc.d:12
16 1 char[] D main src/profilegc.d:34
16 1 char[] D main src/profilegc.d:36
16 1 closure profilegc.main.foo src/profilegc.d:45
Expand All @@ -15,5 +16,5 @@ bytes allocated, allocations, type, function, file:line
16 1 int[] D main src/profilegc.d:14
16 1 int[] D main src/profilegc.d:22
16 1 int[] D main src/profilegc.d:37
16 1 profilegc.main.C D main src/profilegc.d:12
16 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2752
16 1 wchar[] D main src/profilegc.d:35
3 changes: 2 additions & 1 deletion druntime/test/profile/myprofilegc.log.osx.64.exp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ bytes allocated, allocations, type, function, file:line
64 1 double[] profilegc.main src/profilegc.d:56
48 1 float[] D main src/profilegc.d:42
48 1 int[] D main src/profilegc.d:41
32 1 profilegc.main.C D main src/profilegc.d:12
32 1 C D main src/profilegc.d:12
32 1 profilegc.main.C core.lifetime._d_newclassT!(C)._d_newclassT ../../src/core/lifetime.d:2752
32 1 void[] profilegc.main src/profilegc.d:55
16 1 char[] D main src/profilegc.d:34
16 1 char[] D main src/profilegc.d:36
Expand Down