blob: 98621594bd267ab564b93041d8a78c21e94f539a [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/struct.c
*/
#include "root/dsystem.h"
#include "root/root.h"
#include "errors.h"
#include "aggregate.h"
#include "scope.h"
#include "mtype.h"
#include "init.h"
#include "declaration.h"
#include "module.h"
#include "id.h"
#include "statement.h"
#include "template.h"
#include "tokens.h"
#include "target.h"
#include "utf.h"
#include "root/ctfloat.h"
Type *getTypeInfoType(Loc loc, Type *t, Scope *sc);
void unSpeculative(Scope *sc, RootObject *o);
bool MODimplicitConv(MOD modfrom, MOD modto);
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals
FuncDeclaration *StructDeclaration::xerrcmp; // object.xopCmp
/***************************************
* Search toString member function for TypeInfo_Struct.
* string toString();
*/
FuncDeclaration *search_toString(StructDeclaration *sd)
{
Dsymbol *s = search_function(sd, Id::tostring);
FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL;
if (fd)
{
static TypeFunction *tftostring;
if (!tftostring)
{
tftostring = new TypeFunction(ParameterList(), Type::tstring, LINKd);
tftostring = tftostring->merge()->toTypeFunction();
}
fd = fd->overloadExactMatch(tftostring);
}
return fd;
}
/***************************************
* Request additonal semantic analysis for TypeInfo generation.
*/
void semanticTypeInfo(Scope *sc, Type *t)
{
class FullTypeInfoVisitor : public Visitor
{
public:
Scope *sc;
void visit(Type *t)
{
Type *tb = t->toBasetype();
if (tb != t)
tb->accept(this);
}
void visit(TypeNext *t)
{
if (t->next)
t->next->accept(this);
}
void visit(TypeBasic *) { }
void visit(TypeVector *t)
{
t->basetype->accept(this);
}
void visit(TypeAArray *t)
{
t->index->accept(this);
visit((TypeNext *)t);
}
void visit(TypeFunction *t)
{
visit((TypeNext *)t);
// Currently TypeInfo_Function doesn't store parameter types.
}
void visit(TypeStruct *t)
{
//printf("semanticTypeInfo::visit(TypeStruct = %s)\n", t->toChars());
StructDeclaration *sd = t->sym;
/* Step 1: create TypeInfoDeclaration
*/
if (!sc) // inline may request TypeInfo.
{
Scope scx;
scx._module = sd->getModule();
getTypeInfoType(sd->loc, t, &scx);
sd->requestTypeInfo = true;
}
else if (!sc->minst)
{
// don't yet have to generate TypeInfo instance if
// the typeid(T) expression exists in speculative scope.
}
else
{
getTypeInfoType(sd->loc, t, sc);
sd->requestTypeInfo = true;
// Bugzilla 15149, if the typeid operand type comes from a
// result of auto function, it may be yet speculative.
// unSpeculative(sc, sd);
}
/* Step 2: If the TypeInfo generation requires sd.semantic3, run it later.
* This should be done even if typeid(T) exists in speculative scope.
* Because it may appear later in non-speculative scope.
*/
if (!sd->members)
return; // opaque struct
if (!sd->xeq && !sd->xcmp && !sd->postblit &&
!sd->dtor && !sd->xhash && !search_toString(sd))
return; // none of TypeInfo-specific members
// If the struct is in a non-root module, run semantic3 to get
// correct symbols for the member function.
if (sd->semanticRun >= PASSsemantic3)
{
// semantic3 is already done
}
else if (TemplateInstance *ti = sd->isInstantiated())
{
if (ti->minst && !ti->minst->isRoot())
Module::addDeferredSemantic3(sd);
}
else
{
if (sd->inNonRoot())
{
//printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd->toChars(), sd->inNonRoot());
Module::addDeferredSemantic3(sd);
}
}
}
void visit(TypeClass *) { }
void visit(TypeTuple *t)
{
if (t->arguments)
{
for (size_t i = 0; i < t->arguments->length; i++)
{
Type *tprm = (*t->arguments)[i]->type;
if (tprm)
tprm->accept(this);
}
}
}
};
if (sc)
{
if (!sc->func)
return;
if (sc->intypeof)
return;
if (sc->flags & (SCOPEctfe | SCOPEcompile))
return;
}
FullTypeInfoVisitor v;
v.sc = sc;
t->accept(&v);
}
/********************************* AggregateDeclaration ****************************/
AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id)
: ScopeDsymbol(id)
{
this->loc = loc;
storage_class = 0;
protection = Prot(Prot::public_);
type = NULL;
structsize = 0; // size of struct
alignsize = 0; // size of struct for alignment purposes
sizeok = SIZEOKnone; // size not determined yet
deferred = NULL;
isdeprecated = false;
classKind = ClassKind::d;
inv = NULL;
aggNew = NULL;
aggDelete = NULL;
stag = NULL;
sinit = NULL;
enclosing = NULL;
vthis = NULL;
ctor = NULL;
defaultCtor = NULL;
aliasthis = NULL;
noDefaultCtor = false;
dtor = NULL;
getRTInfo = NULL;
}
Prot AggregateDeclaration::prot()
{
return protection;
}
/***************************************
* Create a new scope from sc.
* semantic, semantic2 and semantic3 will use this for aggregate members.
*/
Scope *AggregateDeclaration::newScope(Scope *sc)
{
Scope *sc2 = sc->push(this);
sc2->stc &= STCsafe | STCtrusted | STCsystem;
sc2->parent = this;
if (isUnionDeclaration())
sc2->inunion = 1;
sc2->protection = Prot(Prot::public_);
sc2->explicitProtection = 0;
sc2->aligndecl = NULL;
sc2->userAttribDecl = NULL;
return sc2;
}
void AggregateDeclaration::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().
// Also see https://issues.dlang.org/show_bug.cgi?id=16607
if (semanticRun < PASSsemanticdone)
ScopeDsymbol::setScope(sc);
}
/***************************************
* Find all instance fields, then push them into `fields`.
*
* Runs semantic() for all instance field variables, but also
* the field types can reamin yet not resolved forward references,
* except direct recursive definitions.
* After the process sizeok is set to SIZEOKfwd.
*
* Returns:
* false if any errors occur.
*/
bool AggregateDeclaration::determineFields()
{
if (_scope)
dsymbolSemantic(this, NULL);
if (sizeok != SIZEOKnone)
return true;
//printf("determineFields() %s, fields.length = %d\n", toChars(), fields.length);
fields.setDim(0);
struct SV
{
AggregateDeclaration *agg;
static int func(Dsymbol *s, void *param)
{
VarDeclaration *v = s->isVarDeclaration();
if (!v)
return 0;
if (v->storage_class & STCmanifest)
return 0;
AggregateDeclaration *ad = ((SV *)param)->agg;
if (v->semanticRun < PASSsemanticdone)
dsymbolSemantic(v, NULL);
// Note: Aggregate fields or size could have determined during v->semantic.
if (ad->sizeok != SIZEOKnone)
return 1;
if (v->aliassym)
return 0; // If this variable was really a tuple, skip it.
if (v->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCctfe | STCtemplateparameter))
return 0;
if (!v->isField() || v->semanticRun < PASSsemanticdone)
return 1; // unresolvable forward reference
ad->fields.push(v);
if (v->storage_class & STCref)
return 0;
Type *tv = v->type->baseElemOf();
if (tv->ty != Tstruct)
return 0;
if (ad == ((TypeStruct *)tv)->sym)
{
const char *psz = (v->type->toBasetype()->ty == Tsarray) ? "static array of " : "";
ad->error("cannot have field %s with %ssame struct type", v->toChars(), psz);
ad->type = Type::terror;
ad->errors = true;
return 1;
}
return 0;
}
};
SV sv;
sv.agg = this;
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
if (s->apply(&SV::func, &sv))
{
if (sizeok != SIZEOKnone)
return true;
return false;
}
}
if (sizeok != SIZEOKdone)
sizeok = SIZEOKfwd;
return true;
}
/***************************************
* Collect all instance fields, then determine instance size.
* Returns:
* false if failed to determine the size.
*/
bool AggregateDeclaration::determineSize(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 == SIZEOKdone)
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 (ClassDeclaration *cd = isClassDeclaration())
{
cd = cd->baseClass;
if (cd && !cd->determineSize(loc))
goto Lfail;
}
// Determine instance fields when sizeok == SIZEOKnone
if (!determineFields())
goto Lfail;
if (sizeok != SIZEOKdone)
finalizeSize();
// this aggregate type has:
if (type->ty == Terror)
return false; // marked as invalid during the finalizing.
if (sizeok == SIZEOKdone)
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;
}
void StructDeclaration::semanticTypeInfoMembers()
{
if (xeq &&
xeq->_scope &&
xeq->semanticRun < PASSsemantic3done)
{
unsigned errors = global.startGagging();
semantic3(xeq, xeq->_scope);
if (global.endGagging(errors))
xeq = xerreq;
}
if (xcmp &&
xcmp->_scope &&
xcmp->semanticRun < PASSsemantic3done)
{
unsigned errors = global.startGagging();
semantic3(xcmp, xcmp->_scope);
if (global.endGagging(errors))
xcmp = xerrcmp;
}
FuncDeclaration *ftostr = search_toString(this);
if (ftostr &&
ftostr->_scope &&
ftostr->semanticRun < PASSsemantic3done)
{
semantic3(ftostr, ftostr->_scope);
}
if (xhash &&
xhash->_scope &&
xhash->semanticRun < PASSsemantic3done)
{
semantic3(xhash, xhash->_scope);
}
if (postblit &&
postblit->_scope &&
postblit->semanticRun < PASSsemantic3done)
{
semantic3(postblit, postblit->_scope);
}
if (dtor &&
dtor->_scope &&
dtor->semanticRun < PASSsemantic3done)
{
semantic3(dtor, dtor->_scope);
}
}
d_uns64 AggregateDeclaration::size(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;
}
Type *AggregateDeclaration::getType()
{
return type;
}
bool AggregateDeclaration::isDeprecated()
{
return isdeprecated;
}
bool AggregateDeclaration::isExport() const
{
return protection.kind == Prot::export_;
}
/***************************************
* 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.
*/
bool AggregateDeclaration::checkOverlappedFields()
{
//printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
assert(sizeok == SIZEOKdone);
size_t nfields = fields.length;
if (isNested())
{
ClassDeclaration *cd = isClassDeclaration();
if (!cd || !cd->baseClass || !cd->baseClass->isNested())
nfields--;
}
bool errors = false;
// Fill in missing any elements with default initializers
for (size_t i = 0; i < nfields; i++)
{
VarDeclaration *vd = fields[i];
if (vd->errors)
{
errors = true;
continue;
}
VarDeclaration *vx = vd;
if (vd->_init && vd->_init->isVoidInitializer())
vx = NULL;
// Find overlapped fields with the hole [vd->offset .. vd->offset->size()].
for (size_t j = 0; j < nfields; j++)
{
if (i == j)
continue;
VarDeclaration *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 (!vx)
continue;
if (v2->_init && v2->_init->isVoidInitializer())
continue;
if (vx->_init && v2->_init)
{
::error(loc, "overlapping default initialization for field %s and %s", v2->toChars(), vd->toChars());
errors = true;
}
}
}
return errors;
}
/***************************************
* Fill out remainder of elements[] with default initializers for fields[].
* Input:
* 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[].
*/
bool AggregateDeclaration::fill(Loc loc, Expressions *elements, bool ctorinit)
{
//printf("AggregateDeclaration::fill() %s\n", toChars());
assert(sizeok == SIZEOKdone);
assert(elements);
size_t nfields = fields.length - isNested();
bool errors = false;
size_t dim = elements->length;
elements->setDim(nfields);
for (size_t i = dim; i < nfields; i++)
(*elements)[i] = NULL;
// Fill in missing any elements with default initializers
for (size_t i = 0; i < nfields; i++)
{
if ((*elements)[i])
continue;
VarDeclaration *vd = fields[i];
VarDeclaration *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;
for (size_t j = 0; j < nfields; j++)
{
if (i == j)
continue;
VarDeclaration *v2 = fields[j];
if (!vd->isOverlappedWith(v2))
continue;
if ((*elements)[j])
{
vx = NULL;
break;
}
if (v2->_init && v2->_init->isVoidInitializer())
continue;
if (1)
{
/* 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
{
// Will fix Bugzilla 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;
e = NULL;
}
else
e = vx->getConstInitializer(false);
}
else
{
if ((vx->storage_class & STCnodefaultctor) && !ctorinit)
{
::error(loc, "field %s.%s must be initialized because it has no default constructor",
type->toChars(), vx->toChars());
errors = true;
}
/* Bugzilla 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.
*/
while (telem->toBasetype()->ty == Tsarray)
telem = ((TypeSArray *)telem->toBasetype())->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;
}
}
for (size_t i = 0; i < elements->length; i++)
{
Expression *e = (*elements)[i];
if (e && e->op == TOKerror)
return false;
}
return !errors;
}
/****************************
* Do byte or word alignment as necessary.
* Align sizes of 0, as we may not know array sizes yet.
*
* alignment: struct alignment that is in effect
* size: alignment requirement of field
*/
void AggregateDeclaration::alignmember(
structalign_t alignment,
unsigned size,
unsigned *poffset)
{
//printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
switch (alignment)
{
case (structalign_t) 1:
// No alignment
break;
case (structalign_t) STRUCTALIGN_DEFAULT:
// Alignment in target.fieldalignsize must match what the
// corresponding C compiler's default alignment behavior is.
assert(size > 0 && !(size & (size - 1)));
*poffset = (*poffset + size - 1) & ~(size - 1);
break;
default:
// Align on alignment boundary, which must be a positive power of 2
assert(alignment > 0 && !(alignment & (alignment - 1)));
*poffset = (*poffset + alignment - 1) & ~(alignment - 1);
break;
}
}
/****************************************
* Place a member (mem) into an aggregate (agg), which can be a struct, union or class
* Returns:
* offset to place field at
*
* nextoffset: next location in aggregate
* memsize: size of member
* memalignsize: natural alignment of member
* alignment: alignment in effect for this member
* paggsize: size of aggregate (updated)
* paggalignsize: alignment of aggregate (updated)
* isunion: the aggregate is a union
*/
unsigned AggregateDeclaration::placeField(
unsigned *nextoffset,
unsigned memsize,
unsigned memalignsize,
structalign_t alignment,
unsigned *paggsize,
unsigned *paggalignsize,
bool isunion
)
{
unsigned ofs = *nextoffset;
const unsigned actualAlignment =
alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment;
alignmember(alignment, memalignsize, &ofs);
unsigned memoffset = ofs;
ofs += memsize;
if (ofs > *paggsize)
*paggsize = ofs;
if (!isunion)
*nextoffset = ofs;
if (*paggalignsize < actualAlignment)
*paggalignsize = actualAlignment;
return memoffset;
}
/****************************************
* Returns true if there's an extra member which is the 'this'
* pointer to the enclosing context (enclosing aggregate or function)
*/
bool AggregateDeclaration::isNested()
{
return enclosing != NULL;
}
/* Append vthis field (this->tupleof[$-1]) to make this aggregate type nested.
*/
void AggregateDeclaration::makeNested()
{
if (enclosing) // if already nested
return;
if (sizeok == SIZEOKdone)
return;
if (isUnionDeclaration() || isInterfaceDeclaration())
return;
if (storage_class & STCstatic)
return;
// If nested struct, add in hidden 'this' pointer to outer scope
Dsymbol *s = toParent2();
if (!s)
return;
Type *t = NULL;
if (FuncDeclaration *fd = s->isFuncDeclaration())
{
enclosing = fd;
/* Bugzilla 14422: If a nested class parent is a function, its
* context pointer (== `outer`) should be void* always.
*/
t = Type::tvoidptr;
}
else if (AggregateDeclaration *ad = s->isAggregateDeclaration())
{
if (isClassDeclaration() && ad->isClassDeclaration())
{
enclosing = ad;
}
else if (isStructDeclaration())
{
if (TemplateInstance *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 |= STCref;
// Emulate vthis->addMember()
members->push(vthis);
// Emulate vthis->semantic()
vthis->storage_class |= STCfield;
vthis->parent = this;
vthis->protection = Prot(Prot::public_);
vthis->alignment = t->alignment();
vthis->semanticRun = PASSsemanticdone;
if (sizeok == SIZEOKfwd)
fields.push(vthis);
}
}
/*******************************************
* Look for constructor declaration.
*/
Dsymbol *AggregateDeclaration::searchCtor()
{
Dsymbol *s = search(Loc(), 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
{
static int fp(Dsymbol *s, void *)
{
CtorDeclaration *f = s->isCtorDeclaration();
if (f && f->semanticRun == PASSinit)
dsymbolSemantic(f, NULL);
return 0;
}
};
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *sm = (*members)[i];
sm->apply(&SearchCtor::fp, NULL);
}
}
return s;
}
/********************************* StructDeclaration ****************************/
StructDeclaration::StructDeclaration(Loc loc, Identifier *id, bool inObject)
: AggregateDeclaration(loc, id)
{
zeroInit = 0; // assume false until we do semantic processing
hasIdentityAssign = false;
hasIdentityEquals = false;
postblit = NULL;
xeq = NULL;
xcmp = NULL;
xhash = NULL;
alignment = 0;
ispod = ISPODfwd;
arg1type = NULL;
arg2type = NULL;
requestTypeInfo = false;
// For forward references
type = new TypeStruct(this);
if (inObject)
{
if (id == Id::ModuleInfo && !Module::moduleinfo)
Module::moduleinfo = this;
}
}
StructDeclaration *StructDeclaration::create(Loc loc, Identifier *id, bool inObject)
{
return new StructDeclaration(loc, id, inObject);
}
Dsymbol *StructDeclaration::syntaxCopy(Dsymbol *s)
{
StructDeclaration *sd =
s ? (StructDeclaration *)s
: new StructDeclaration(loc, ident, false);
return ScopeDsymbol::syntaxCopy(sd);
}
Dsymbol *StructDeclaration::search(const Loc &loc, Identifier *ident, int flags)
{
//printf("%s.StructDeclaration::search('%s', flags = x%x)\n", toChars(), ident->toChars(), flags);
if (_scope && !symtab)
dsymbolSemantic(this, _scope);
if (!members || !symtab) // opaque or semantic() is not yet called
{
error("is forward referenced when looking for `%s`", ident->toChars());
return NULL;
}
return ScopeDsymbol::search(loc, ident, flags);
}
/**********************************
* Determine if exp is all binary zeros.
* Params:
* exp = expression to check
* Returns:
* true if it's all binary 0
*/
static bool isZeroInit(Expression *exp)
{
switch (exp->op)
{
case TOKint64:
return exp->toInteger() == 0;
case TOKnull:
case TOKfalse:
return true;
case TOKstructliteral:
{
StructLiteralExp *sle = (StructLiteralExp *) exp;
for (size_t i = 0; i < sle->sd->fields.length; i++)
{
VarDeclaration *field = sle->sd->fields[i];
if (field->type->size(field->loc))
{
Expression *e = (*sle->elements)[i];
if (e ? !isZeroInit(e)
: !field->type->isZeroInit(field->loc))
return false;
}
}
return true;
}
case TOKarrayliteral:
{
ArrayLiteralExp *ale = (ArrayLiteralExp *) exp;
const size_t dim = ale->elements ? ale->elements->length : 0;
if (ale->type->toBasetype()->ty == Tarray) // if initializing a dynamic array
return dim == 0;
for (size_t i = 0; i < dim; i++)
{
if (!isZeroInit(ale->getElement(i)))
return false;
}
/* Note that true is returned for all T[0]
*/
return true;
}
case TOKstring:
{
StringExp *se = exp->toStringExp();
if (se->type->toBasetype()->ty == Tarray) // if initializing a dynamic array
return se->len == 0;
void *s = se->string;
for (size_t i = 0; i < se->len; i++)
{
dinteger_t val;
switch (se->sz)
{
case 1: val = (( utf8_t *)s)[i]; break;
case 2: val = ((utf16_t *)s)[i]; break;
case 4: val = ((utf32_t *)s)[i]; break;
default: assert(0); break;
}
if (val)
return false;
}
return true;
}
case TOKvector:
{
VectorExp *ve = (VectorExp *) exp;
return isZeroInit(ve->e1);
}
case TOKfloat64:
case TOKcomplex80:
{
return (exp->toReal() == CTFloat::zero) &&
(exp->toImaginary() == CTFloat::zero);
}
default:
return false;
}
}
void StructDeclaration::finalizeSize()
{
//printf("StructDeclaration::finalizeSize() %s, sizeok = %d\n", toChars(), sizeok);
assert(sizeok != SIZEOKdone);
//printf("+StructDeclaration::finalizeSize() %s, fields.length = %d, sizeok = %d\n", toChars(), fields.length, sizeok);
fields.setDim(0); // workaround
// Set the offsets of the fields and determine the size of the struct
unsigned offset = 0;
bool isunion = isUnionDeclaration() != NULL;
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
s->setFieldOffset(this, &offset, isunion);
}
if (type->ty == Terror)
return;
// 0 sized struct's are set to 1 byte
if (structsize == 0)
{
structsize = 1;
alignsize = 1;
}
// Round struct size up to next alignsize boundary.
// This will ensure that arrays of structs will get their internals
// aligned properly.
if (alignment == STRUCTALIGN_DEFAULT)
structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
else
structsize = (structsize + alignment - 1) & ~(alignment - 1);
sizeok = SIZEOKdone;
//printf("-StructDeclaration::finalizeSize() %s, fields.length = %d, structsize = %d\n", toChars(), fields.length, structsize);
if (errors)
return;
// Calculate fields[i]->overlapped
if (checkOverlappedFields())
{
errors = true;
return;
}
// Determine if struct is all zeros or not
zeroInit = 1;
for (size_t i = 0; i < fields.length; i++)
{
VarDeclaration *vd = fields[i];
if (vd->_init)
{
if (vd->_init->isVoidInitializer())
/* Treat as 0 for the purposes of putting the initializer
* in the BSS segment, or doing a mass set to 0
*/
continue;
// Zero size fields are zero initialized
if (vd->type->size(vd->loc) == 0)
continue;
// Examine init to see if it is all 0s.
Expression *exp = vd->getConstInitializer();
if (!exp || !isZeroInit(exp))
{
zeroInit = 0;
break;
}
}
else if (!vd->type->isZeroInit(loc))
{
zeroInit = 0;
break;
}
}
TypeTuple *tt = target.toArgTypes(type);
size_t dim = tt ? tt->arguments->length : 0;
if (dim >= 1)
{
assert(dim <= 2);
arg1type = (*tt->arguments)[0]->type;
if (dim == 2)
arg2type = (*tt->arguments)[1]->type;
}
}
/***************************************
* Fit elements[] to the corresponding type of field[].
* Input:
* loc
* sc
* elements The explicit arguments that given to construct object.
* stype The constructed object type.
* Returns false if any errors occur.
* Otherwise, returns true and elements[] are rewritten for the output.
*/
bool StructDeclaration::fit(Loc loc, Scope *sc, Expressions *elements, Type *stype)
{
if (!elements)
return true;
size_t nfields = fields.length - isNested();
size_t offset = 0;
for (size_t i = 0; i < elements->length; i++)
{
Expression *e = (*elements)[i];
if (!e)
continue;
e = resolveProperties(sc, e);
if (i >= nfields)
{
if (i == fields.length - 1 && isNested() && e->op == TOKnull)
{
// CTFE sometimes creates null as hidden pointer; we'll allow this.
continue;
}
::error(loc, "more initializers than fields (%d) of %s", (int)nfields, toChars());
return false;
}
VarDeclaration *v = fields[i];
if (v->offset < offset)
{
::error(loc, "overlapping initialization for %s", v->toChars());
return false;
}
offset = (unsigned)(v->offset + v->type->size());
Type *t = v->type;
if (stype)
t = t->addMod(stype->mod);
Type *origType = t;
Type *tb = t->toBasetype();
/* Look for case of initializing a static array with a too-short
* string literal, such as:
* char[5] foo = "abc";
* Allow this by doing an explicit cast, which will lengthen the string
* literal.
*/
if (e->op == TOKstring && tb->ty == Tsarray)
{
StringExp *se = (StringExp *)e;
Type *typeb = se->type->toBasetype();
TY tynto = tb->nextOf()->ty;
if (!se->committed &&
(typeb->ty == Tarray || typeb->ty == Tsarray) &&
(tynto == Tchar || tynto == Twchar || tynto == Tdchar) &&
se->numberOfCodeUnits(tynto) < ((TypeSArray *)tb)->dim->toInteger())
{
e = se->castTo(sc, t);
goto L1;
}
}
while (!e->implicitConvTo(t) && tb->ty == Tsarray)
{
/* Static array initialization, as in:
* T[3][5] = e;
*/
t = tb->nextOf();
tb = t->toBasetype();
}
if (!e->implicitConvTo(t))
t = origType; // restore type for better diagnostic
e = e->implicitCastTo(sc, t);
L1:
if (e->op == TOKerror)
return false;
(*elements)[i] = doCopyOrMove(sc, e);
}
return true;
}
/***************************************
* Return true if struct is POD (Plain Old Data).
* This is defined as:
* not nested
* no postblits, destructors, or assignment operators
* no 'ref' fields or fields that are themselves non-POD
* The idea being these are compatible with C structs.
*/
bool StructDeclaration::isPOD()
{
// If we've already determined whether this struct is POD.
if (ispod != ISPODfwd)
return (ispod == ISPODyes);
ispod = ISPODyes;
if (enclosing || postblit || dtor)
ispod = ISPODno;
// Recursively check all fields are POD.
for (size_t i = 0; i < fields.length; i++)
{
VarDeclaration *v = fields[i];
if (v->storage_class & STCref)
{
ispod = ISPODno;
break;
}
Type *tv = v->type->baseElemOf();
if (tv->ty == Tstruct)
{
TypeStruct *ts = (TypeStruct *)tv;
StructDeclaration *sd = ts->sym;
if (!sd->isPOD())
{
ispod = ISPODno;
break;
}
}
}
return (ispod == ISPODyes);
}
const char *StructDeclaration::kind() const
{
return "struct";
}
/********************************* UnionDeclaration ****************************/
UnionDeclaration::UnionDeclaration(Loc loc, Identifier *id)
: StructDeclaration(loc, id, false)
{
}
Dsymbol *UnionDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
UnionDeclaration *ud = new UnionDeclaration(loc, ident);
return StructDeclaration::syntaxCopy(ud);
}
const char *UnionDeclaration::kind() const
{
return "union";
}