8sa1-gcc/gcc/d/dmd/dstruct.c
Iain Buclaw a3b38b7781 d: Merge upstream dmd 7132b3537
Splits out all semantic passes for Dsymbol, Type, and TemplateParameter
nodes into Visitors in separate files, and the copyright years of all
sources have been updated.

Reviewed-on: https://github.com/dlang/dmd/pull/12190

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd 7132b3537.
	* Make-lang.in (D_FRONTEND_OBJS): Add d/dsymbolsem.o, d/semantic2.o,
	d/semantic3.o, and d/templateparamsem.o.
	* d-compiler.cc (Compiler::genCmain): Update calls to semantic
	entrypoint functions.
	* d-lang.cc (d_parse_file): Likewise.
	* typeinfo.cc (make_frontend_typeinfo): Likewise.
2021-02-13 12:50:45 +01:00

1302 lines
37 KiB
C

/* 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 (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";
}