817 lines
27 KiB
D
817 lines
27 KiB
D
/**
|
|
* Defines a `Dsymbol` representing an aggregate, which is a `struct`, `union` or `class`.
|
|
*
|
|
* Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions),
|
|
* $(LINK2 https://dlang.org/spec/class.html, Class).
|
|
*
|
|
* Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
|
|
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
|
|
* License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d, _aggregate.d)
|
|
* Documentation: https://dlang.org/phobos/dmd_aggregate.html
|
|
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aggregate.d
|
|
*/
|
|
|
|
module dmd.aggregate;
|
|
|
|
import core.stdc.stdio;
|
|
import core.checkedint;
|
|
|
|
import dmd.aliasthis;
|
|
import dmd.apply;
|
|
import dmd.arraytypes;
|
|
import dmd.astenums;
|
|
import dmd.attrib;
|
|
import dmd.declaration;
|
|
import dmd.dscope;
|
|
import dmd.dstruct;
|
|
import dmd.dsymbol;
|
|
import dmd.dsymbolsem;
|
|
import dmd.dtemplate;
|
|
import dmd.errors;
|
|
import dmd.expression;
|
|
import dmd.func;
|
|
import dmd.globals;
|
|
import dmd.id;
|
|
import dmd.identifier;
|
|
import dmd.mtype;
|
|
import dmd.tokens;
|
|
import dmd.typesem : defaultInit;
|
|
import dmd.visitor;
|
|
|
|
/**
|
|
* The ClassKind enum is used in AggregateDeclaration AST nodes to
|
|
* specify the linkage type of the struct/class/interface or if it
|
|
* is an anonymous class. If the class is anonymous it is also
|
|
* considered to be a D class.
|
|
*/
|
|
enum ClassKind : ubyte
|
|
{
|
|
/// the aggregate is a d(efault) class
|
|
d,
|
|
/// the aggregate is a C++ struct/class/interface
|
|
cpp,
|
|
/// the aggregate is an Objective-C class/interface
|
|
objc,
|
|
/// the aggregate is a C struct
|
|
c,
|
|
}
|
|
|
|
/**
|
|
* Give a nice string for a class kind for error messages
|
|
* Params:
|
|
* c = class kind
|
|
* Returns:
|
|
* 0-terminated string for `c`
|
|
*/
|
|
const(char)* toChars(ClassKind c)
|
|
{
|
|
final switch (c)
|
|
{
|
|
case ClassKind.d:
|
|
return "D";
|
|
case ClassKind.cpp:
|
|
return "C++";
|
|
case ClassKind.objc:
|
|
return "Objective-C";
|
|
case ClassKind.c:
|
|
return "C";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If an aggregate has a pargma(mangle, ...) this holds the information
|
|
* to mangle.
|
|
*/
|
|
struct MangleOverride
|
|
{
|
|
Dsymbol agg; // The symbol to copy template parameters from (if any)
|
|
Identifier id; // the name to override the aggregate's with, defaults to agg.ident
|
|
}
|
|
|
|
/***********************************************************
|
|
* Abstract aggregate as a common ancestor for Class- and StructDeclaration.
|
|
*/
|
|
extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
|
|
{
|
|
Type type; ///
|
|
StorageClass storage_class; ///
|
|
uint structsize; /// size of struct
|
|
uint alignsize; /// size of struct for alignment purposes
|
|
VarDeclarations fields; /// VarDeclaration fields
|
|
Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol
|
|
|
|
/// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
|
|
ClassKind classKind;
|
|
/// Specify whether to mangle the aggregate as a `class` or a `struct`
|
|
/// This information is used by the MSVC mangler
|
|
/// Only valid for class and struct. TODO: Merge with ClassKind ?
|
|
CPPMANGLE cppmangle;
|
|
|
|
/// overridden symbol with pragma(mangle, "...") if not null
|
|
MangleOverride* mangleOverride;
|
|
|
|
/**
|
|
* !=null if is nested
|
|
* pointing to the dsymbol that directly enclosing it.
|
|
* 1. The function that enclosing it (nested struct and class)
|
|
* 2. The class that enclosing it (nested class only)
|
|
* 3. If enclosing aggregate is template, its enclosing dsymbol.
|
|
*
|
|
* See AggregateDeclaraton::makeNested for the details.
|
|
*/
|
|
Dsymbol enclosing;
|
|
|
|
VarDeclaration vthis; /// 'this' parameter if this aggregate is nested
|
|
VarDeclaration vthis2; /// 'this' parameter if this aggregate is a template and is nested
|
|
|
|
// Special member functions
|
|
FuncDeclarations invs; /// Array of invariants
|
|
FuncDeclaration inv; /// Merged invariant calling all members of invs
|
|
|
|
/// CtorDeclaration or TemplateDeclaration
|
|
Dsymbol ctor;
|
|
|
|
/// default constructor - should have no arguments, because
|
|
/// it would be stored in TypeInfo_Class.defaultConstructor
|
|
CtorDeclaration defaultCtor;
|
|
|
|
AliasThis aliasthis; /// forward unresolved lookups to aliasthis
|
|
|
|
DtorDeclarations userDtors; /// user-defined destructors (`~this()`) - mixins can yield multiple ones
|
|
DtorDeclaration aggrDtor; /// aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes)
|
|
DtorDeclaration dtor; /// the aggregate destructor exposed as `__xdtor` alias
|
|
/// (same as aggrDtor, except for C++ classes with virtual dtor on Windows)
|
|
DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
|
|
DtorDeclaration fieldDtor; /// function destructing (non-inherited) fields
|
|
|
|
Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
|
|
|
|
///
|
|
Visibility visibility;
|
|
bool noDefaultCtor; /// no default construction
|
|
bool disableNew; /// disallow allocations using `new`
|
|
Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data
|
|
|
|
final extern (D) this(const ref Loc loc, Identifier id)
|
|
{
|
|
super(loc, id);
|
|
visibility = Visibility(Visibility.Kind.public_);
|
|
}
|
|
|
|
/***************************************
|
|
* Create a new scope from sc.
|
|
* semantic, semantic2 and semantic3 will use this for aggregate members.
|
|
*/
|
|
Scope* newScope(Scope* sc)
|
|
{
|
|
auto sc2 = sc.push(this);
|
|
sc2.stc &= STC.flowThruAggregate;
|
|
sc2.parent = this;
|
|
sc2.inunion = isUnionDeclaration();
|
|
sc2.visibility = Visibility(Visibility.Kind.public_);
|
|
sc2.explicitVisibility = 0;
|
|
sc2.aligndecl = null;
|
|
sc2.userAttribDecl = null;
|
|
sc2.namespace = null;
|
|
return sc2;
|
|
}
|
|
|
|
override final void setScope(Scope* sc)
|
|
{
|
|
// Might need a scope to resolve forward references. The check for
|
|
// semanticRun prevents unnecessary setting of _scope during deferred
|
|
// setScope phases for aggregates which already finished semantic().
|
|
// See https://issues.dlang.org/show_bug.cgi?id=16607
|
|
if (semanticRun < PASS.semanticdone)
|
|
ScopeDsymbol.setScope(sc);
|
|
}
|
|
|
|
/***************************************
|
|
* Returns:
|
|
* The total number of fields minus the number of hidden fields.
|
|
*/
|
|
final size_t nonHiddenFields()
|
|
{
|
|
return fields.dim - isNested() - (vthis2 !is null);
|
|
}
|
|
|
|
/***************************************
|
|
* Collect all instance fields, then determine instance size.
|
|
* Returns:
|
|
* false if failed to determine the size.
|
|
*/
|
|
final bool determineSize(const ref Loc loc)
|
|
{
|
|
//printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
|
|
|
|
// The previous instance size finalizing had:
|
|
if (type.ty == Terror)
|
|
return false; // failed already
|
|
if (sizeok == Sizeok.done)
|
|
return true; // succeeded
|
|
|
|
if (!members)
|
|
{
|
|
error(loc, "unknown size");
|
|
return false;
|
|
}
|
|
|
|
if (_scope)
|
|
dsymbolSemantic(this, null);
|
|
|
|
// Determine the instance size of base class first.
|
|
if (auto cd = isClassDeclaration())
|
|
{
|
|
cd = cd.baseClass;
|
|
if (cd && !cd.determineSize(loc))
|
|
goto Lfail;
|
|
}
|
|
|
|
// Determine instance fields when sizeok == Sizeok.none
|
|
if (!this.determineFields())
|
|
goto Lfail;
|
|
if (sizeok != Sizeok.done)
|
|
finalizeSize();
|
|
|
|
// this aggregate type has:
|
|
if (type.ty == Terror)
|
|
return false; // marked as invalid during the finalizing.
|
|
if (sizeok == Sizeok.done)
|
|
return true; // succeeded to calculate instance size.
|
|
|
|
Lfail:
|
|
// There's unresolvable forward reference.
|
|
if (type != Type.terror)
|
|
error(loc, "no size because of forward reference");
|
|
// Don't cache errors from speculative semantic, might be resolvable later.
|
|
// https://issues.dlang.org/show_bug.cgi?id=16574
|
|
if (!global.gag)
|
|
{
|
|
type = Type.terror;
|
|
errors = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
abstract void finalizeSize();
|
|
|
|
override final uinteger_t size(const ref Loc loc)
|
|
{
|
|
//printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
|
|
bool ok = determineSize(loc);
|
|
//printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
|
|
return ok ? structsize : SIZE_INVALID;
|
|
}
|
|
|
|
/***************************************
|
|
* Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
|
|
* field initializers have unique memory space on instance.
|
|
* Returns:
|
|
* true if any errors happen.
|
|
*/
|
|
extern (D) final bool checkOverlappedFields()
|
|
{
|
|
//printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
|
|
assert(sizeok == Sizeok.done);
|
|
size_t nfields = fields.dim;
|
|
if (isNested())
|
|
{
|
|
auto cd = isClassDeclaration();
|
|
if (!cd || !cd.baseClass || !cd.baseClass.isNested())
|
|
nfields--;
|
|
if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
|
|
nfields--;
|
|
}
|
|
bool errors = false;
|
|
|
|
// Fill in missing any elements with default initializers
|
|
foreach (i; 0 .. nfields)
|
|
{
|
|
auto vd = fields[i];
|
|
if (vd.errors)
|
|
{
|
|
errors = true;
|
|
continue;
|
|
}
|
|
|
|
const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
|
|
|
|
// Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
|
|
foreach (j; 0 .. nfields)
|
|
{
|
|
if (i == j)
|
|
continue;
|
|
auto v2 = fields[j];
|
|
if (v2.errors)
|
|
{
|
|
errors = true;
|
|
continue;
|
|
}
|
|
if (!vd.isOverlappedWith(v2))
|
|
continue;
|
|
|
|
// vd and v2 are overlapping.
|
|
vd.overlapped = true;
|
|
v2.overlapped = true;
|
|
|
|
if (!MODimplicitConv(vd.type.mod, v2.type.mod))
|
|
v2.overlapUnsafe = true;
|
|
if (!MODimplicitConv(v2.type.mod, vd.type.mod))
|
|
vd.overlapUnsafe = true;
|
|
|
|
if (i > j)
|
|
continue;
|
|
|
|
if (!v2._init)
|
|
continue;
|
|
|
|
if (v2._init.isVoidInitializer())
|
|
continue;
|
|
|
|
if (vd._init && !vdIsVoidInit && v2._init)
|
|
{
|
|
.error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
|
|
errors = true;
|
|
}
|
|
else if (v2._init && i < j)
|
|
{
|
|
.error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
|
|
v2.toChars(), v2._init.toChars(), vd.toChars());
|
|
errors = true;
|
|
}
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
/***************************************
|
|
* Fill out remainder of elements[] with default initializers for fields[].
|
|
* Params:
|
|
* loc = location
|
|
* elements = explicit arguments which given to construct object.
|
|
* ctorinit = true if the elements will be used for default initialization.
|
|
* Returns:
|
|
* false if any errors occur.
|
|
* Otherwise, returns true and the missing arguments will be pushed in elements[].
|
|
*/
|
|
final bool fill(const ref Loc loc, Expressions* elements, bool ctorinit)
|
|
{
|
|
//printf("AggregateDeclaration::fill() %s\n", toChars());
|
|
assert(sizeok == Sizeok.done);
|
|
assert(elements);
|
|
const nfields = nonHiddenFields();
|
|
bool errors = false;
|
|
|
|
size_t dim = elements.dim;
|
|
elements.setDim(nfields);
|
|
foreach (size_t i; dim .. nfields)
|
|
(*elements)[i] = null;
|
|
|
|
// Fill in missing any elements with default initializers
|
|
foreach (i; 0 .. nfields)
|
|
{
|
|
if ((*elements)[i])
|
|
continue;
|
|
|
|
auto vd = fields[i];
|
|
auto vx = vd;
|
|
if (vd._init && vd._init.isVoidInitializer())
|
|
vx = null;
|
|
|
|
// Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
|
|
size_t fieldi = i;
|
|
foreach (j; 0 .. nfields)
|
|
{
|
|
if (i == j)
|
|
continue;
|
|
auto v2 = fields[j];
|
|
if (!vd.isOverlappedWith(v2))
|
|
continue;
|
|
|
|
if ((*elements)[j])
|
|
{
|
|
vx = null;
|
|
break;
|
|
}
|
|
if (v2._init && v2._init.isVoidInitializer())
|
|
continue;
|
|
|
|
version (all)
|
|
{
|
|
/* Prefer first found non-void-initialized field
|
|
* union U { int a; int b = 2; }
|
|
* U u; // Error: overlapping initialization for field a and b
|
|
*/
|
|
if (!vx)
|
|
{
|
|
vx = v2;
|
|
fieldi = j;
|
|
}
|
|
else if (v2._init)
|
|
{
|
|
.error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
|
|
errors = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always
|
|
|
|
/* Prefer explicitly initialized field
|
|
* union U { int a; int b = 2; }
|
|
* U u; // OK (u.b == 2)
|
|
*/
|
|
if (!vx || !vx._init && v2._init)
|
|
{
|
|
vx = v2;
|
|
fieldi = j;
|
|
}
|
|
else if (vx != vd && !vx.isOverlappedWith(v2))
|
|
{
|
|
// Both vx and v2 fills vd, but vx and v2 does not overlap
|
|
}
|
|
else if (vx._init && v2._init)
|
|
{
|
|
.error(loc, "overlapping default initialization for field `%s` and `%s`",
|
|
v2.toChars(), vd.toChars());
|
|
errors = true;
|
|
}
|
|
else
|
|
assert(vx._init || !vx._init && !v2._init);
|
|
}
|
|
}
|
|
if (vx)
|
|
{
|
|
Expression e;
|
|
if (vx.type.size() == 0)
|
|
{
|
|
e = null;
|
|
}
|
|
else if (vx._init)
|
|
{
|
|
assert(!vx._init.isVoidInitializer());
|
|
if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057
|
|
{
|
|
vx.error(loc, "recursive initialization of field");
|
|
errors = true;
|
|
}
|
|
else
|
|
e = vx.getConstInitializer(false);
|
|
}
|
|
else
|
|
{
|
|
if ((vx.storage_class & STC.nodefaultctor) && !ctorinit)
|
|
{
|
|
.error(loc, "field `%s.%s` must be initialized because it has no default constructor",
|
|
type.toChars(), vx.toChars());
|
|
errors = true;
|
|
}
|
|
/* https://issues.dlang.org/show_bug.cgi?id=12509
|
|
* Get the element of static array type.
|
|
*/
|
|
Type telem = vx.type;
|
|
if (telem.ty == Tsarray)
|
|
{
|
|
/* We cannot use Type::baseElemOf() here.
|
|
* If the bottom of the Tsarray is an enum type, baseElemOf()
|
|
* will return the base of the enum, and its default initializer
|
|
* would be different from the enum's.
|
|
*/
|
|
TypeSArray tsa;
|
|
while ((tsa = telem.toBasetype().isTypeSArray()) !is null)
|
|
telem = tsa.next;
|
|
if (telem.ty == Tvoid)
|
|
telem = Type.tuns8.addMod(telem.mod);
|
|
}
|
|
if (telem.needsNested() && ctorinit)
|
|
e = telem.defaultInit(loc);
|
|
else
|
|
e = telem.defaultInitLiteral(loc);
|
|
}
|
|
(*elements)[fieldi] = e;
|
|
}
|
|
}
|
|
foreach (e; *elements)
|
|
{
|
|
if (e && e.op == EXP.error)
|
|
return false;
|
|
}
|
|
|
|
return !errors;
|
|
}
|
|
|
|
/****************************
|
|
* Do byte or word alignment as necessary.
|
|
* Align sizes of 0, as we may not know array sizes yet.
|
|
* Params:
|
|
* alignment = struct alignment that is in effect
|
|
* memalignsize = natural alignment of field
|
|
* poffset = pointer to offset to be aligned
|
|
*/
|
|
extern (D) static void alignmember(structalign_t alignment, uint memalignsize, uint* poffset) pure nothrow @safe
|
|
{
|
|
//debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, *poffset);
|
|
uint alignvalue;
|
|
|
|
if (alignment.isDefault())
|
|
{
|
|
// Alignment in Target::fieldalignsize must match what the
|
|
// corresponding C compiler's default alignment behavior is.
|
|
alignvalue = memalignsize;
|
|
}
|
|
else if (alignment.isPack()) // #pragma pack semantics
|
|
{
|
|
alignvalue = alignment.get();
|
|
if (memalignsize < alignvalue)
|
|
alignvalue = memalignsize; // align to min(memalignsize, alignment)
|
|
}
|
|
else if (alignment.get() > 1)
|
|
{
|
|
// Align on alignment boundary, which must be a positive power of 2
|
|
alignvalue = alignment.get();
|
|
}
|
|
else
|
|
return;
|
|
|
|
assert(alignvalue > 0 && !(alignvalue & (alignvalue - 1)));
|
|
*poffset = (*poffset + alignvalue - 1) & ~(alignvalue - 1);
|
|
}
|
|
|
|
/****************************************
|
|
* Place a field (mem) into an aggregate (agg), which can be a struct, union or class
|
|
* Params:
|
|
* nextoffset = location just past the end of the previous field in the aggregate.
|
|
* Updated to be just past the end of this field to be placed, i.e. the future nextoffset
|
|
* memsize = size of field
|
|
* memalignsize = natural alignment of field
|
|
* alignment = alignment in effect for this field
|
|
* paggsize = size of aggregate (updated)
|
|
* paggalignsize = alignment of aggregate (updated)
|
|
* isunion = the aggregate is a union
|
|
* Returns:
|
|
* aligned offset to place field at
|
|
*
|
|
*/
|
|
extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize,
|
|
structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion)
|
|
{
|
|
uint ofs = *nextoffset;
|
|
|
|
const uint actualAlignment =
|
|
alignment.isDefault() || alignment.isPack() && memalignsize < alignment.get()
|
|
? memalignsize : alignment.get();
|
|
|
|
// Ensure no overflow
|
|
bool overflow;
|
|
const sz = addu(memsize, actualAlignment, overflow);
|
|
addu(ofs, sz, overflow);
|
|
if (overflow) assert(0);
|
|
|
|
// Skip no-op for noreturn without custom aligment
|
|
if (memalignsize != 0 || !alignment.isDefault())
|
|
alignmember(alignment, memalignsize, &ofs);
|
|
|
|
uint memoffset = ofs;
|
|
ofs += memsize;
|
|
if (ofs > *paggsize)
|
|
*paggsize = ofs;
|
|
if (!isunion)
|
|
*nextoffset = ofs;
|
|
|
|
if (*paggalignsize < actualAlignment)
|
|
*paggalignsize = actualAlignment;
|
|
|
|
return memoffset;
|
|
}
|
|
|
|
override final Type getType()
|
|
{
|
|
/* Apply storage classes to forward references. (Issue 22254)
|
|
* Note: Avoid interfaces for now. Implementing qualifiers on interface
|
|
* definitions exposed some issues in their TypeInfo generation in DMD.
|
|
* Related PR: https://github.com/dlang/dmd/pull/13312
|
|
*/
|
|
if (semanticRun == PASS.initial && !isInterfaceDeclaration())
|
|
{
|
|
auto stc = storage_class;
|
|
if (_scope)
|
|
stc |= _scope.stc;
|
|
type = type.addSTC(stc);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
// is aggregate deprecated?
|
|
override final bool isDeprecated() const
|
|
{
|
|
return !!(this.storage_class & STC.deprecated_);
|
|
}
|
|
|
|
/// Flag this aggregate as deprecated
|
|
final void setDeprecated()
|
|
{
|
|
this.storage_class |= STC.deprecated_;
|
|
}
|
|
|
|
/****************************************
|
|
* Returns true if there's an extra member which is the 'this'
|
|
* pointer to the enclosing context (enclosing aggregate or function)
|
|
*/
|
|
final bool isNested() const
|
|
{
|
|
return enclosing !is null;
|
|
}
|
|
|
|
/* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
|
|
*/
|
|
extern (D) final void makeNested()
|
|
{
|
|
if (enclosing) // if already nested
|
|
return;
|
|
if (sizeok == Sizeok.done)
|
|
return;
|
|
if (isUnionDeclaration() || isInterfaceDeclaration())
|
|
return;
|
|
if (storage_class & STC.static_)
|
|
return;
|
|
|
|
// If nested struct, add in hidden 'this' pointer to outer scope
|
|
auto s = toParentLocal();
|
|
if (!s)
|
|
s = toParent2();
|
|
if (!s)
|
|
return;
|
|
Type t = null;
|
|
if (auto fd = s.isFuncDeclaration())
|
|
{
|
|
enclosing = fd;
|
|
|
|
/* https://issues.dlang.org/show_bug.cgi?id=14422
|
|
* If a nested class parent is a function, its
|
|
* context pointer (== `outer`) should be void* always.
|
|
*/
|
|
t = Type.tvoidptr;
|
|
}
|
|
else if (auto ad = s.isAggregateDeclaration())
|
|
{
|
|
if (isClassDeclaration() && ad.isClassDeclaration())
|
|
{
|
|
enclosing = ad;
|
|
}
|
|
else if (isStructDeclaration())
|
|
{
|
|
if (auto ti = ad.parent.isTemplateInstance())
|
|
{
|
|
enclosing = ti.enclosing;
|
|
}
|
|
}
|
|
t = ad.handleType();
|
|
}
|
|
if (enclosing)
|
|
{
|
|
//printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
|
|
assert(t);
|
|
if (t.ty == Tstruct)
|
|
t = Type.tvoidptr; // t should not be a ref type
|
|
|
|
assert(!vthis);
|
|
vthis = new ThisDeclaration(loc, t);
|
|
//vthis.storage_class |= STC.ref_;
|
|
|
|
// Emulate vthis.addMember()
|
|
members.push(vthis);
|
|
|
|
// Emulate vthis.dsymbolSemantic()
|
|
vthis.storage_class |= STC.field;
|
|
vthis.parent = this;
|
|
vthis.visibility = Visibility(Visibility.Kind.public_);
|
|
vthis.alignment = t.alignment();
|
|
vthis.semanticRun = PASS.semanticdone;
|
|
|
|
if (sizeok == Sizeok.fwd)
|
|
fields.push(vthis);
|
|
|
|
makeNested2();
|
|
}
|
|
}
|
|
|
|
/* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
|
|
*/
|
|
extern (D) final void makeNested2()
|
|
{
|
|
if (vthis2)
|
|
return;
|
|
if (!vthis)
|
|
makeNested(); // can't add second before first
|
|
if (!vthis)
|
|
return;
|
|
if (sizeok == Sizeok.done)
|
|
return;
|
|
if (isUnionDeclaration() || isInterfaceDeclaration())
|
|
return;
|
|
if (storage_class & STC.static_)
|
|
return;
|
|
|
|
auto s0 = toParentLocal();
|
|
auto s = toParent2();
|
|
if (!s || !s0 || s == s0)
|
|
return;
|
|
auto cd = s.isClassDeclaration();
|
|
Type t = cd ? cd.type : Type.tvoidptr;
|
|
|
|
vthis2 = new ThisDeclaration(loc, t);
|
|
//vthis2.storage_class |= STC.ref_;
|
|
|
|
// Emulate vthis2.addMember()
|
|
members.push(vthis2);
|
|
|
|
// Emulate vthis2.dsymbolSemantic()
|
|
vthis2.storage_class |= STC.field;
|
|
vthis2.parent = this;
|
|
vthis2.visibility = Visibility(Visibility.Kind.public_);
|
|
vthis2.alignment = t.alignment();
|
|
vthis2.semanticRun = PASS.semanticdone;
|
|
|
|
if (sizeok == Sizeok.fwd)
|
|
fields.push(vthis2);
|
|
}
|
|
|
|
override final bool isExport() const
|
|
{
|
|
return visibility.kind == Visibility.Kind.export_;
|
|
}
|
|
|
|
/*******************************************
|
|
* Look for constructor declaration.
|
|
*/
|
|
final Dsymbol searchCtor()
|
|
{
|
|
auto s = search(Loc.initial, Id.ctor);
|
|
if (s)
|
|
{
|
|
if (!(s.isCtorDeclaration() ||
|
|
s.isTemplateDeclaration() ||
|
|
s.isOverloadSet()))
|
|
{
|
|
s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation");
|
|
errors = true;
|
|
s = null;
|
|
}
|
|
}
|
|
if (s && s.toParent() != this)
|
|
s = null; // search() looks through ancestor classes
|
|
if (s)
|
|
{
|
|
// Finish all constructors semantics to determine this.noDefaultCtor.
|
|
struct SearchCtor
|
|
{
|
|
extern (C++) static int fp(Dsymbol s, void* ctxt)
|
|
{
|
|
auto f = s.isCtorDeclaration();
|
|
if (f && f.semanticRun == PASS.initial)
|
|
f.dsymbolSemantic(null);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < members.dim; i++)
|
|
{
|
|
auto sm = (*members)[i];
|
|
sm.apply(&SearchCtor.fp, null);
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
override final Visibility visible() pure nothrow @nogc @safe
|
|
{
|
|
return visibility;
|
|
}
|
|
|
|
// 'this' type
|
|
final Type handleType()
|
|
{
|
|
return type;
|
|
}
|
|
|
|
// Does this class have an invariant function?
|
|
final bool hasInvariant()
|
|
{
|
|
return invs.length != 0;
|
|
}
|
|
|
|
// Back end
|
|
void* sinit; /// initializer symbol
|
|
|
|
override final inout(AggregateDeclaration) isAggregateDeclaration() inout
|
|
{
|
|
return this;
|
|
}
|
|
|
|
override void accept(Visitor v)
|
|
{
|
|
v.visit(this);
|
|
}
|
|
}
|