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.
1302 lines
37 KiB
C
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";
|
|
}
|