Showing with 63 additions and 10 deletions.
  1. +37 −4 std/conv.d
  2. +13 −0 std/experimental/allocator/package.d
  3. +13 −6 std/traits.d
41 changes: 37 additions & 4 deletions std/conv.d
Original file line number Diff line number Diff line change
Expand Up @@ -4273,6 +4273,11 @@ Given a raw memory area $(D chunk), constructs an object of $(D class)
type $(D T) at that address. The constructor is passed the arguments
$(D Args).
If `T` is an inner class whose `outer` field can be used to access an instance
of the enclosing class, then `Args` must not be empty, and the first member of it
must be a valid initializer for that `outer` field. Correct initialization of
this field is essential to access members of the outer class inside `T` methods.
Preconditions:
$(D chunk) must be at least as large as $(D T) needs
and should have an alignment multiple of $(D T)'s alignment. (The size
Expand All @@ -4298,18 +4303,30 @@ if (is(T == class))
// Initialize the object in its pre-ctor state
chunk[0 .. classSize] = typeid(T).initializer[];

static if (isInnerClass!T)
{
static assert(Args.length > 0,
"Initializing an inner class requires a pointer to the outer class");
static assert(is(Args[0] : typeof(T.outer)),
"The first argument must be a pointer to the outer class");

result.outer = args[0];
alias args1 = args[1..$];
}
else alias args1 = args;

// Call the ctor if any
static if (is(typeof(result.__ctor(args))))
static if (is(typeof(result.__ctor(args1))))
{
// T defines a genuine constructor accepting args
// Go the classic route: write .init first, then call ctor
result.__ctor(args);
result.__ctor(args1);
}
else
{
static assert(args.length == 0 && !is(typeof(&T.__ctor)),
static assert(args1.length == 0 && !is(typeof(&T.__ctor)),
"Don't know how to initialize an object of type "
~ T.stringof ~ " with arguments " ~ Args.stringof);
~ T.stringof ~ " with arguments " ~ typeof(args1).stringof);
}
return result;
}
Expand All @@ -4327,6 +4344,22 @@ if (is(T == class))
assert(c.i == 5);
}

@system unittest
{
class Outer
{
int i = 3;
class Inner
{
auto getI() { return i; }
}
}
auto outerBuf = new void[__traits(classInstanceSize, Outer)];
auto innerBuf = new void[__traits(classInstanceSize, Outer.Inner)];
auto inner = innerBuf.emplace!(Outer.Inner)(outerBuf.emplace!Outer);
assert(inner.getI == 3);
}

@nogc pure nothrow unittest
{
int var = 6;
Expand Down
13 changes: 13 additions & 0 deletions std/experimental/allocator/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,19 @@ unittest
assert(cust.id == uint.max); // default initialized
cust = theAllocator.make!Customer(42);
assert(cust.id == 42);

// explicit passing of outer pointer
static class Outer
{
int x = 3;
class Inner
{
auto getX() { return x; }
}
}
auto outer = theAllocator.make!Outer();
auto inner = theAllocator.make!(Outer.Inner)(outer);
assert(outer.x == inner.getX);
}

unittest // bugzilla 15639 & 15772
Expand Down
19 changes: 13 additions & 6 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
* $(LREF hasNested)
* $(LREF hasUnsharedAliasing)
* $(LREF InterfacesTuple)
* $(LREF isInnerClass)
* $(LREF isNested)
* $(LREF MemberFunctionsTuple)
* $(LREF RepresentationTypeTuple)
Expand Down Expand Up @@ -2036,8 +2037,11 @@ template isInnerClass(T)
{
import std.meta : staticIndexOf;

enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T))
&& (staticIndexOf!(__traits(allMembers, T), "outer") == -1);
static if (is(typeof(T.outer)))
enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T))
&& (staticIndexOf!(__traits(allMembers, T), "outer") == -1);
else
enum isInnerClass = false;
}

///
Expand All @@ -2051,15 +2055,18 @@ template isInnerClass(T)

class Outer1
{
class Inner
class Inner1 { }
class Inner2
{
int outer;
}
}
static assert(isInnerClass!(Outer1.Inner));
static assert(isInnerClass!(Outer1.Inner1));
static assert(!isInnerClass!(Outer1.Inner2));

class Outer2
static class Outer2
{
class Inner
static class Inner
{
int outer;
}
Expand Down