diff --git a/compiler/src/dmd/e2ir.d b/compiler/src/dmd/e2ir.d index 56dd58a90d79..45b7a79fce6f 100644 --- a/compiler/src/dmd/e2ir.d +++ b/compiler/src/dmd/e2ir.d @@ -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()) diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index 8c4d2710cfa9..c72995e6d935 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -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()); @@ -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)); diff --git a/compiler/src/dmd/expression.h b/compiler/src/dmd/expression.h index f491c1d9c27e..bfd595c28b2d 100644 --- a/compiler/src/dmd/expression.h +++ b/compiler/src/dmd/expression.h @@ -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; diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index c2afa297a3b6..6dfdbd4bb4d7 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -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()) { diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 5cc035758b84..ae44e58a51aa 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -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* arguments); NewExp* syntaxCopy() override; void accept(Visitor* v) override; @@ -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; diff --git a/compiler/src/dmd/id.d b/compiler/src/dmd/id.d index 73fde18ed11e..ec5cb25ef28a 100644 --- a/compiler/src/dmd/id.d +++ b/compiler/src/dmd/id.d @@ -313,6 +313,8 @@ immutable Msgtable[] msgtable = { "__ArrayDtor" }, { "_d_delThrowable" }, { "_d_newThrowable" }, + { "_d_newclassT" }, + { "_d_newclassTTrace" }, { "_d_assert_fail" }, { "dup" }, { "_aaApply" }, diff --git a/compiler/test/compilable/test23431_minimal.d b/compiler/test/compilable/test23431_minimal.d index 0293f1272865..c3ae19a21eb9 100644 --- a/compiler/test/compilable/test23431_minimal.d +++ b/compiler/test/compilable/test23431_minimal.d @@ -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; diff --git a/compiler/test/compilable/test23433.d b/compiler/test/compilable/test23433.d index 713267c0ecbc..dfffa456c8a1 100644 --- a/compiler/test/compilable/test23433.d +++ b/compiler/test/compilable/test23433.d @@ -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 diff --git a/compiler/test/fail_compilation/fail308.d b/compiler/test/fail_compilation/fail308.d index d885b3ef3ee7..603fe51db7b0 100644 --- a/compiler/test/fail_compilation/fail308.d +++ b/compiler/test/fail_compilation/fail308.d @@ -16,6 +16,6 @@ class MinHeap(NodeType) unittest { struct TestType {} - MinHeap!(TestType) foo = new MinHeap!(TestType)(); + MinHeap!(TestType) foo; } } diff --git a/compiler/test/fail_compilation/ice23569.d b/compiler/test/fail_compilation/ice23569.d index 277814f76cfa..6fdb71a39763 100644 --- a/compiler/test/fail_compilation/ice23569.d +++ b/compiler/test/fail_compilation/ice23569.d @@ -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 diff --git a/druntime/src/core/lifetime.d b/druntime/src/core/lifetime.d index 371308def797..848d4b3e2b06 100644 --- a/druntime/src/core/lifetime.d +++ b/druntime/src/core/lifetime.d @@ -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!"); +} diff --git a/druntime/src/object.d b/druntime/src/object.d index d98e698aa630..56e01ace2cb4 100644 --- a/druntime/src/object.d +++ b/druntime/src/object.d @@ -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); diff --git a/druntime/src/rt/tracegc.d b/druntime/src/rt/tracegc.d index 2575a50dab9e..29b61468056d 100644 --- a/druntime/src/rt/tracegc.d +++ b/druntime/src/rt/tracegc.d @@ -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); diff --git a/druntime/test/profile/myprofilegc.log.freebsd.32.exp b/druntime/test/profile/myprofilegc.log.freebsd.32.exp index 7e1a05881c63..bcf256b499c1 100644 --- a/druntime/test/profile/myprofilegc.log.freebsd.32.exp +++ b/druntime/test/profile/myprofilegc.log.freebsd.32.exp @@ -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 @@ -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 diff --git a/druntime/test/profile/myprofilegc.log.freebsd.64.exp b/druntime/test/profile/myprofilegc.log.freebsd.64.exp index a3e184920744..6a779303247e 100644 --- a/druntime/test/profile/myprofilegc.log.freebsd.64.exp +++ b/druntime/test/profile/myprofilegc.log.freebsd.64.exp @@ -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 diff --git a/druntime/test/profile/myprofilegc.log.linux.32.exp b/druntime/test/profile/myprofilegc.log.linux.32.exp index 7e1a05881c63..bcf256b499c1 100644 --- a/druntime/test/profile/myprofilegc.log.linux.32.exp +++ b/druntime/test/profile/myprofilegc.log.linux.32.exp @@ -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 @@ -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 diff --git a/druntime/test/profile/myprofilegc.log.linux.64.exp b/druntime/test/profile/myprofilegc.log.linux.64.exp index a3e184920744..6a779303247e 100644 --- a/druntime/test/profile/myprofilegc.log.linux.64.exp +++ b/druntime/test/profile/myprofilegc.log.linux.64.exp @@ -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 diff --git a/druntime/test/profile/myprofilegc.log.osx.32.exp b/druntime/test/profile/myprofilegc.log.osx.32.exp index a099944c288b..b33b547aee19 100644 --- a/druntime/test/profile/myprofilegc.log.osx.32.exp +++ b/druntime/test/profile/myprofilegc.log.osx.32.exp @@ -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 @@ -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 diff --git a/druntime/test/profile/myprofilegc.log.osx.64.exp b/druntime/test/profile/myprofilegc.log.osx.64.exp index a3e184920744..6a779303247e 100644 --- a/druntime/test/profile/myprofilegc.log.osx.64.exp +++ b/druntime/test/profile/myprofilegc.log.osx.64.exp @@ -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