Skip to content

Commit

Permalink
Translate _d_newclass to a template (#14837)
Browse files Browse the repository at this point in the history
This makes the following changes:
- Add template `_d_newclassT` to `druntime.src.core.lifetime.d`
- Replace lowering of `new C()` to `_d_newclassT!C()`
- Add `lowering` member to `NewExp`. This field stores the above
lowering to be used by e2ir.d
- Keep the old `_d_newclass` hook because it's used by
`TypeInfo_Class.create()`
- Add dummy `_d_newclassT` hook to tests that redefine the `object`
module
- Remove `new MinHeap!(TestType)()` from `fail308.d`. Otherwise the
errror was changed and printed the local path to druntime
- Move `err` to global scope in rt.sections.d to avoid the frontend
lowering
- Account for the `GC.malloc()` called by the template hook in the
`-profile=gc` tests

Signed-off-by: Teodor Dutu <teodor.dutu@gmail.com>

Signed-off-by: Teodor Dutu <teodor.dutu@gmail.com>
  • Loading branch information
teodutu committed Jan 27, 2023
1 parent 72af67c commit 276ef21
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 14 deletions.
5 changes: 1 addition & 4 deletions compiler/src/dmd/e2ir.d
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);
ectype = null;

if (cd.isNested())
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dmd/expression.d
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`

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
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`

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
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
Expand Up @@ -7187,6 +7187,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 @@ -8577,6 +8578,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
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
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
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
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
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
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
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
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
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
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
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
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
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
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

1 comment on commit 276ef21

@WebFreak001
Copy link
Member

Choose a reason for hiding this comment

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

this introduced a regression: https://issues.dlang.org/show_bug.cgi?id=23874

that particular reproduction case has however been resolved by abb7836 already

Can you maybe confirm if this ICE / segfault is likely to just be in the case of static if (is(typeof(new Object()))) and is not going to happen with other code? If it's a more general issue, it'd make sense to resolve the root issue, otherwise I can close the issue.

Please sign in to comment.