blob: 18aa6aa9ab4300a45e846ee88d37f4eb5c548b8a [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/expression.c
*/
#include "root/dsystem.h"
#include "root/rmem.h"
#include "root/root.h"
#include "errors.h"
#include "mtype.h"
#include "init.h"
#include "expression.h"
#include "template.h"
#include "utf.h"
#include "enum.h"
#include "scope.h"
#include "statement.h"
#include "declaration.h"
#include "aggregate.h"
#include "import.h"
#include "id.h"
#include "dsymbol.h"
#include "module.h"
#include "attrib.h"
#include "hdrgen.h"
#include "parse.h"
#include "doc.h"
#include "root/aav.h"
#include "nspace.h"
#include "ctfe.h"
#include "target.h"
bool walkPostorder(Expression *e, StoppableVisitor *v);
bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag);
bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember);
VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false);
char *MODtoChars(MOD mod);
bool MODimplicitConv(MOD modfrom, MOD modto);
void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod);
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg);
void toAutoQualChars(const char **result, Type *t1, Type *t2);
/*****************************************
* Determine if 'this' is available.
* If it is, return the FuncDeclaration that has it.
*/
FuncDeclaration *hasThis(Scope *sc)
{
//printf("hasThis()\n");
Dsymbol *p = sc->parent;
while (p && p->isTemplateMixin())
p = p->parent;
FuncDeclaration *fdthis = p ? p->isFuncDeclaration() : NULL;
//printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis->toChars() : "");
// Go upwards until we find the enclosing member function
FuncDeclaration *fd = fdthis;
while (1)
{
if (!fd)
{
goto Lno;
}
if (!fd->isNested())
break;
Dsymbol *parent = fd->parent;
while (1)
{
if (!parent)
goto Lno;
TemplateInstance *ti = parent->isTemplateInstance();
if (ti)
parent = ti->parent;
else
break;
}
fd = parent->isFuncDeclaration();
}
if (!fd->isThis())
{ //printf("test '%s'\n", fd->toChars());
goto Lno;
}
assert(fd->vthis);
return fd;
Lno:
return NULL; // don't have 'this' available
}
bool isNeedThisScope(Scope *sc, Declaration *d)
{
if (sc->intypeof == 1)
return false;
AggregateDeclaration *ad = d->isThis();
if (!ad)
return false;
//printf("d = %s, ad = %s\n", d->toChars(), ad->toChars());
for (Dsymbol *s = sc->parent; s; s = s->toParent2())
{
//printf("\ts = %s %s, toParent2() = %p\n", s->kind(), s->toChars(), s->toParent2());
if (AggregateDeclaration *ad2 = s->isAggregateDeclaration())
{
if (ad2 == ad)
return false;
else if (ad2->isNested())
continue;
else
return true;
}
if (FuncDeclaration *f = s->isFuncDeclaration())
{
if (f->isMember2())
break;
}
}
return true;
}
/******************************
* check e is exp.opDispatch!(tiargs) or not
* It's used to switch to UFCS the semantic analysis path
*/
bool isDotOpDispatch(Expression *e)
{
return e->op == TOKdotti &&
((DotTemplateInstanceExp *)e)->ti->name == Id::opDispatch;
}
/****************************************
* Expand tuples.
* Input:
* exps aray of Expressions
* Output:
* exps rewritten in place
*/
void expandTuples(Expressions *exps)
{
//printf("expandTuples()\n");
if (exps)
{
for (size_t i = 0; i < exps->length; i++)
{
Expression *arg = (*exps)[i];
if (!arg)
continue;
// Look for tuple with 0 members
if (arg->op == TOKtype)
{
TypeExp *e = (TypeExp *)arg;
if (e->type->toBasetype()->ty == Ttuple)
{
TypeTuple *tt = (TypeTuple *)e->type->toBasetype();
if (!tt->arguments || tt->arguments->length == 0)
{
exps->remove(i);
if (i == exps->length)
return;
i--;
continue;
}
}
}
// Inline expand all the tuples
while (arg->op == TOKtuple)
{
TupleExp *te = (TupleExp *)arg;
exps->remove(i); // remove arg
exps->insert(i, te->exps); // replace with tuple contents
if (i == exps->length)
return; // empty tuple, no more arguments
(*exps)[i] = Expression::combine(te->e0, (*exps)[i]);
arg = (*exps)[i];
}
}
}
}
/****************************************
* Expand alias this tuples.
*/
TupleDeclaration *isAliasThisTuple(Expression *e)
{
if (!e->type)
return NULL;
Type *t = e->type->toBasetype();
Lagain:
if (Dsymbol *s = t->toDsymbol(NULL))
{
AggregateDeclaration *ad = s->isAggregateDeclaration();
if (ad)
{
s = ad->aliasthis;
if (s && s->isVarDeclaration())
{
TupleDeclaration *td = s->isVarDeclaration()->toAlias()->isTupleDeclaration();
if (td && td->isexp)
return td;
}
if (Type *att = t->aliasthisOf())
{
t = att;
goto Lagain;
}
}
}
return NULL;
}
int expandAliasThisTuples(Expressions *exps, size_t starti)
{
if (!exps || exps->length == 0)
return -1;
for (size_t u = starti; u < exps->length; u++)
{
Expression *exp = (*exps)[u];
TupleDeclaration *td = isAliasThisTuple(exp);
if (td)
{
exps->remove(u);
for (size_t i = 0; i<td->objects->length; ++i)
{
Expression *e = isExpression((*td->objects)[i]);
assert(e);
assert(e->op == TOKdsymbol);
DsymbolExp *se = (DsymbolExp *)e;
Declaration *d = se->s->isDeclaration();
assert(d);
e = new DotVarExp(exp->loc, exp, d);
assert(d->type);
e->type = d->type;
exps->insert(u + i, e);
}
return (int)u;
}
}
return -1;
}
/****************************************
* Get TemplateDeclaration enclosing FuncDeclaration.
*/
TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s)
{
FuncDeclaration *f = s->isFuncDeclaration();
if (f && f->parent)
{
TemplateInstance *ti = f->parent->isTemplateInstance();
if (ti && !ti->isTemplateMixin() &&
ti->tempdecl && ((TemplateDeclaration *)ti->tempdecl)->onemember &&
ti->tempdecl->ident == f->ident)
{
return (TemplateDeclaration *)ti->tempdecl;
}
}
return NULL;
}
/************************************************
* If we want the value of this expression, but do not want to call
* the destructor on it.
*/
Expression *valueNoDtor(Expression *e)
{
if (e->op == TOKcall)
{
/* The struct value returned from the function is transferred
* so do not call the destructor on it.
* Recognize:
* ((S _ctmp = S.init), _ctmp).this(...)
* and make sure the destructor is not called on _ctmp
* BUG: if e is a CommaExp, we should go down the right side.
*/
CallExp *ce = (CallExp *)e;
if (ce->e1->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)ce->e1;
if (dve->var->isCtorDeclaration())
{
// It's a constructor call
if (dve->e1->op == TOKcomma)
{
CommaExp *comma = (CommaExp *)dve->e1;
if (comma->e2->op == TOKvar)
{
VarExp *ve = (VarExp *)comma->e2;
VarDeclaration *ctmp = ve->var->isVarDeclaration();
if (ctmp)
{
ctmp->storage_class |= STCnodtor;
assert(!ce->isLvalue());
}
}
}
}
}
}
else if (e->op == TOKvar)
{
VarDeclaration *vtmp = ((VarExp *)e)->var->isVarDeclaration();
if (vtmp && vtmp->storage_class & STCrvalue)
{
vtmp->storage_class |= STCnodtor;
}
}
return e;
}
/*********************************************
* If e is an instance of a struct, and that struct has a copy constructor,
* rewrite e as:
* (tmp = e),tmp
* Input:
* sc just used to specify the scope of created temporary variable
*/
Expression *callCpCtor(Scope *sc, Expression *e)
{
Type *tv = e->type->baseElemOf();
if (tv->ty == Tstruct)
{
StructDeclaration *sd = ((TypeStruct *)tv)->sym;
if (sd->postblit)
{
/* Create a variable tmp, and replace the argument e with:
* (tmp = e),tmp
* and let AssignExp() handle the construction.
* This is not the most efficent, ideally tmp would be constructed
* directly onto the stack.
*/
VarDeclaration *tmp = copyToTemp(STCrvalue, "__copytmp", e);
tmp->storage_class |= STCnodtor;
dsymbolSemantic(tmp, sc);
Expression *de = new DeclarationExp(e->loc, tmp);
Expression *ve = new VarExp(e->loc, tmp);
de->type = Type::tvoid;
ve->type = e->type;
e = Expression::combine(de, ve);
}
}
return e;
}
/************************************************
* Handle the postblit call on lvalue, or the move of rvalue.
*/
Expression *doCopyOrMove(Scope *sc, Expression *e)
{
if (e->op == TOKquestion)
{
CondExp *ce = (CondExp *)e;
ce->e1 = doCopyOrMove(sc, ce->e1);
ce->e2 = doCopyOrMove(sc, ce->e2);
}
else
{
e = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e);
}
return e;
}
/******************************** Expression **************************/
Expression::Expression(Loc loc, TOK op, int size)
{
//printf("Expression::Expression(op = %d) this = %p\n", op, this);
this->loc = loc;
this->op = op;
this->size = (unsigned char)size;
this->parens = 0;
type = NULL;
}
void Expression::_init()
{
CTFEExp::cantexp = new CTFEExp(TOKcantexp);
CTFEExp::voidexp = new CTFEExp(TOKvoidexp);
CTFEExp::breakexp = new CTFEExp(TOKbreak);
CTFEExp::continueexp = new CTFEExp(TOKcontinue);
CTFEExp::gotoexp = new CTFEExp(TOKgoto);
}
Expression *Expression::syntaxCopy()
{
//printf("Expression::syntaxCopy()\n");
//print();
return copy();
}
/*********************************
* Does *not* do a deep copy.
*/
Expression *Expression::copy()
{
Expression *e;
if (!size)
{
assert(0);
}
void *pe = mem.xmalloc(size);
//printf("Expression::copy(op = %d) e = %p\n", op, pe);
e = (Expression *)memcpy(pe, (void *)this, size);
return e;
}
void Expression::print()
{
fprintf(stderr, "%s\n", toChars());
fflush(stderr);
}
const char *Expression::toChars()
{
OutBuffer buf;
HdrGenState hgs;
toCBuffer(this, &buf, &hgs);
return buf.extractChars();
}
void Expression::error(const char *format, ...) const
{
if (type != Type::terror)
{
va_list ap;
va_start(ap, format);
::verror(loc, format, ap);
va_end( ap );
}
}
void Expression::warning(const char *format, ...) const
{
if (type != Type::terror)
{
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
}
void Expression::deprecation(const char *format, ...) const
{
if (type != Type::terror)
{
va_list ap;
va_start(ap, format);
::vdeprecation(loc, format, ap);
va_end( ap );
}
}
/**********************************
* Combine e1 and e2 by CommaExp if both are not NULL.
*/
Expression *Expression::combine(Expression *e1, Expression *e2)
{
if (e1)
{
if (e2)
{
e1 = new CommaExp(e1->loc, e1, e2);
e1->type = e2->type;
}
}
else
e1 = e2;
return e1;
}
/**********************************
* If 'e' is a tree of commas, returns the leftmost expression
* by stripping off it from the tree. The remained part of the tree
* is returned via *pe0.
* Otherwise 'e' is directly returned and *pe0 is set to NULL.
*/
Expression *Expression::extractLast(Expression *e, Expression **pe0)
{
if (e->op != TOKcomma)
{
*pe0 = NULL;
return e;
}
CommaExp *ce = (CommaExp *)e;
if (ce->e2->op != TOKcomma)
{
*pe0 = ce->e1;
return ce->e2;
}
else
{
*pe0 = e;
Expression **pce = &ce->e2;
while (((CommaExp *)(*pce))->e2->op == TOKcomma)
{
pce = &((CommaExp *)(*pce))->e2;
}
assert((*pce)->op == TOKcomma);
ce = (CommaExp *)(*pce);
*pce = ce->e1;
return ce->e2;
}
}
dinteger_t Expression::toInteger()
{
//printf("Expression %s\n", Token::toChars(op));
error("integer constant expression expected instead of %s", toChars());
return 0;
}
uinteger_t Expression::toUInteger()
{
//printf("Expression %s\n", Token::toChars(op));
return (uinteger_t)toInteger();
}
real_t Expression::toReal()
{
error("floating point constant expression expected instead of %s", toChars());
return CTFloat::zero;
}
real_t Expression::toImaginary()
{
error("floating point constant expression expected instead of %s", toChars());
return CTFloat::zero;
}
complex_t Expression::toComplex()
{
error("floating point constant expression expected instead of %s", toChars());
return complex_t(CTFloat::zero);
}
StringExp *Expression::toStringExp()
{
return NULL;
}
TupleExp *Expression::toTupleExp()
{
return NULL;
}
/***************************************
* Return !=0 if expression is an lvalue.
*/
bool Expression::isLvalue()
{
return false;
}
/*******************************
* Give error if we're not an lvalue.
* If we can, convert expression to be an lvalue.
*/
Expression *Expression::toLvalue(Scope *, Expression *e)
{
if (!e)
e = this;
else if (!loc.filename)
loc = e->loc;
if (e->op == TOKtype)
error("%s `%s` is a type, not an lvalue", e->type->kind(), e->type->toChars());
else
error("%s is not an lvalue", e->toChars());
return new ErrorExp();
}
/***************************************
* Parameters:
* sc: scope
* flag: 1: do not issue error message for invalid modification
* Returns:
* 0: is not modifiable
* 1: is modifiable in default == being related to type->isMutable()
* 2: is modifiable, because this is a part of initializing.
*/
int Expression::checkModifiable(Scope *, int)
{
return type ? 1 : 0; // default modifiable
}
Expression *Expression::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type->toChars());
// See if this expression is a modifiable lvalue (i.e. not const)
if (checkModifiable(sc) == 1)
{
assert(type);
if (!type->isMutable())
{
error("cannot modify %s expression %s", MODtoChars(type->mod), toChars());
return new ErrorExp();
}
else if (!type->isAssignable())
{
error("cannot modify struct %s %s with immutable members", toChars(), type->toChars());
return new ErrorExp();
}
}
return toLvalue(sc, e);
}
/****************************************
* Check that the expression has a valid type.
* If not, generates an error "... has no type".
* Returns:
* true if the expression is not valid.
* Note:
* When this function returns true, `checkValue()` should also return true.
*/
bool Expression::checkType()
{
return false;
}
/****************************************
* Check that the expression has a valid value.
* If not, generates an error "... has no value".
* Returns:
* true if the expression is not valid or has void type.
*/
bool Expression::checkValue()
{
if (type && type->toBasetype()->ty == Tvoid)
{
error("expression %s is void and has no value", toChars());
//print(); halt();
if (!global.gag)
type = Type::terror;
return true;
}
return false;
}
bool Expression::checkScalar()
{
if (op == TOKerror)
return true;
if (type->toBasetype()->ty == Terror)
return true;
if (!type->isscalar())
{
error("`%s` is not a scalar, it is a %s", toChars(), type->toChars());
return true;
}
return checkValue();
}
bool Expression::checkNoBool()
{
if (op == TOKerror)
return true;
if (type->toBasetype()->ty == Terror)
return true;
if (type->toBasetype()->ty == Tbool)
{
error("operation not allowed on bool `%s`", toChars());
return true;
}
return false;
}
bool Expression::checkIntegral()
{
if (op == TOKerror)
return true;
if (type->toBasetype()->ty == Terror)
return true;
if (!type->isintegral())
{
error("`%s` is not of integral type, it is a %s", toChars(), type->toChars());
return true;
}
return checkValue();
}
bool Expression::checkArithmetic()
{
if (op == TOKerror)
return true;
if (type->toBasetype()->ty == Terror)
return true;
if (!type->isintegral() && !type->isfloating())
{
error("`%s` is not of arithmetic type, it is a %s", toChars(), type->toChars());
return true;
}
return checkValue();
}
bool Expression::checkDeprecated(Scope *sc, Dsymbol *s)
{
return s->checkDeprecated(loc, sc);
}
bool Expression::checkDisabled(Scope *sc, Dsymbol *s)
{
if (Declaration *d = s->isDeclaration())
{
return d->checkDisabled(loc, sc);
}
return false;
}
/*********************************************
* Calling function f.
* Check the purity, i.e. if we're in a pure function
* we can only call other pure functions.
* Returns true if error occurs.
*/
bool Expression::checkPurity(Scope *sc, FuncDeclaration *f)
{
if (!sc->func)
return false;
if (sc->func == f)
return false;
if (sc->intypeof == 1)
return false;
if (sc->flags & (SCOPEctfe | SCOPEdebug))
return false;
/* Given:
* void f() {
* pure void g() {
* /+pure+/ void h() {
* /+pure+/ void i() { }
* }
* }
* }
* g() can call h() but not f()
* i() can call h() and g() but not f()
*/
// Find the closest pure parent of the calling function
FuncDeclaration *outerfunc = sc->func;
FuncDeclaration *calledparent = f;
if (outerfunc->isInstantiated())
{
// The attributes of outerfunc should be inferred from the call of f.
}
else if (f->isInstantiated())
{
// The attributes of f are inferred from its body.
}
else if (f->isFuncLiteralDeclaration())
{
// The attributes of f are always inferred in its declared place.
}
else
{
/* Today, static local functions are impure by default, but they cannot
* violate purity of enclosing functions.
*
* auto foo() pure { // non instantiated funciton
* static auto bar() { // static, without pure attribute
* impureFunc(); // impure call
* // Although impureFunc is called inside bar, f(= impureFunc)
* // is not callable inside pure outerfunc(= foo <- bar).
* }
*
* bar();
* // Although bar is called inside foo, f(= bar) is callable
* // bacause calledparent(= foo) is same with outerfunc(= foo).
* }
*/
while (outerfunc->toParent2() &&
outerfunc->isPureBypassingInference() == PUREimpure &&
outerfunc->toParent2()->isFuncDeclaration())
{
outerfunc = outerfunc->toParent2()->isFuncDeclaration();
if (outerfunc->type->ty == Terror)
return true;
}
while (calledparent->toParent2() &&
calledparent->isPureBypassingInference() == PUREimpure &&
calledparent->toParent2()->isFuncDeclaration())
{
calledparent = calledparent->toParent2()->isFuncDeclaration();
if (calledparent->type->ty == Terror)
return true;
}
}
// If the caller has a pure parent, then either the called func must be pure,
// OR, they must have the same pure parent.
if (!f->isPure() && calledparent != outerfunc)
{
FuncDeclaration *ff = outerfunc;
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure())
{
error("pure %s `%s` cannot call impure %s `%s`",
ff->kind(), ff->toPrettyChars(), f->kind(), f->toPrettyChars());
return true;
}
}
return false;
}
/*******************************************
* Accessing variable v.
* Check for purity and safety violations.
* Returns true if error occurs.
*/
bool Expression::checkPurity(Scope *sc, VarDeclaration *v)
{
//printf("v = %s %s\n", v->type->toChars(), v->toChars());
/* Look for purity and safety violations when accessing variable v
* from current function.
*/
if (!sc->func)
return false;
if (sc->intypeof == 1)
return false; // allow violations inside typeof(expression)
if (sc->flags & (SCOPEctfe | SCOPEdebug))
return false; // allow violations inside compile-time evaluated expressions and debug conditionals
if (v->ident == Id::ctfe)
return false; // magic variable never violates pure and safe
if (v->isImmutable())
return false; // always safe and pure to access immutables...
if (v->isConst() && !v->isRef() && (v->isDataseg() || v->isParameter()) &&
v->type->implicitConvTo(v->type->immutableOf()))
return false; // or const global/parameter values which have no mutable indirections
if (v->storage_class & STCmanifest)
return false; // ...or manifest constants
bool err = false;
if (v->isDataseg())
{
// Bugzilla 7533: Accessing implicit generated __gate is pure.
if (v->ident == Id::gate)
return false;
/* Accessing global mutable state.
* Therefore, this function and all its immediately enclosing
* functions must be pure.
*/
/* Today, static local functions are impure by default, but they cannot
* violate purity of enclosing functions.
*
* auto foo() pure { // non instantiated funciton
* static auto bar() { // static, without pure attribute
* globalData++; // impure access
* // Although globalData is accessed inside bar,
* // it is not accessible inside pure foo.
* }
* }
*/
for (Dsymbol *s = sc->func; s; s = s->toParent2())
{
FuncDeclaration *ff = s->isFuncDeclaration();
if (!ff)
break;
if (sc->flags & SCOPEcompile ? ff->isPureBypassingInference() >= PUREweak : ff->setImpure())
{
error("pure %s `%s` cannot access mutable static data `%s`",
ff->kind(), ff->toPrettyChars(), v->toChars());
err = true;
break;
}
/* If the enclosing is an instantiated function or a lambda, its
* attribute inference result is preferred.
*/
if (ff->isInstantiated())
break;
if (ff->isFuncLiteralDeclaration())
break;
}
}
else
{
/* Given:
* void f() {
* int fx;
* pure void g() {
* int gx;
* /+pure+/ void h() {
* int hx;
* /+pure+/ void i() { }
* }
* }
* }
* i() can modify hx and gx but not fx
*/
Dsymbol *vparent = v->toParent2();
for (Dsymbol *s = sc->func; !err && s; s = s->toParent2())
{
if (s == vparent)
break;
if (AggregateDeclaration *ad = s->isAggregateDeclaration())
{
if (ad->isNested())
continue;
break;
}
FuncDeclaration *ff = s->isFuncDeclaration();
if (!ff)
break;
if (ff->isNested() || ff->isThis())
{
if (ff->type->isImmutable() ||
(ff->type->isShared() && !MODimplicitConv(ff->type->mod, v->type->mod)))
{
OutBuffer ffbuf;
OutBuffer vbuf;
MODMatchToBuffer(&ffbuf, ff->type->mod, v->type->mod);
MODMatchToBuffer(&vbuf, v->type->mod, ff->type->mod);
error("%s%s `%s` cannot access %sdata `%s`",
ffbuf.peekChars(), ff->kind(), ff->toPrettyChars(), vbuf.peekChars(), v->toChars());
err = true;
break;
}
continue;
}
break;
}
}
/* Do not allow safe functions to access __gshared data
*/
if (v->storage_class & STCgshared)
{
if (sc->func->setUnsafe())
{
error("safe %s `%s` cannot access __gshared data `%s`",
sc->func->kind(), sc->func->toChars(), v->toChars());
err = true;
}
}
return err;
}
/*********************************************
* Calling function f.
* Check the safety, i.e. if we're in a @safe function
* we can only call @safe or @trusted functions.
* Returns true if error occurs.
*/
bool Expression::checkSafety(Scope *sc, FuncDeclaration *f)
{
if (!sc->func)
return false;
if (sc->func == f)
return false;
if (sc->intypeof == 1)
return false;
if (sc->flags & SCOPEctfe)
return false;
if (!f->isSafe() && !f->isTrusted())
{
if (sc->flags & SCOPEcompile ? sc->func->isSafeBypassingInference() : sc->func->setUnsafe())
{
if (loc.linnum == 0) // e.g. implicitly generated dtor
loc = sc->func->loc;
error("@safe %s `%s` cannot call @system %s `%s`",
sc->func->kind(), sc->func->toPrettyChars(), f->kind(), f->toPrettyChars());
return true;
}
}
return false;
}
/*********************************************
* Calling function f.
* Check the @nogc-ness, i.e. if we're in a @nogc function
* we can only call other @nogc functions.
* Returns true if error occurs.
*/
bool Expression::checkNogc(Scope *sc, FuncDeclaration *f)
{
if (!sc->func)
return false;
if (sc->func == f)
return false;
if (sc->intypeof == 1)
return false;
if (sc->flags & SCOPEctfe)
return false;
if (!f->isNogc())
{
if (sc->flags & SCOPEcompile ? sc->func->isNogcBypassingInference() : sc->func->setGC())
{
if (loc.linnum == 0) // e.g. implicitly generated dtor
loc = sc->func->loc;
error("@nogc %s `%s` cannot call non-@nogc %s `%s`",
sc->func->kind(), sc->func->toPrettyChars(), f->kind(), f->toPrettyChars());
return true;
}
}
return false;
}
/********************************************
* Check that the postblit is callable if t is an array of structs.
* Returns true if error happens.
*/
bool Expression::checkPostblit(Scope *sc, Type *t)
{
t = t->baseElemOf();
if (t->ty == Tstruct)
{
if (global.params.useTypeInfo && Type::dtypeinfo)
{
// Bugzilla 11395: Require TypeInfo generation for array concatenation
semanticTypeInfo(sc, t);
}
StructDeclaration *sd = ((TypeStruct *)t)->sym;
if (sd->postblit)
{
if (sd->postblit->checkDisabled(loc, sc))
return true;
//checkDeprecated(sc, sd->postblit); // necessary?
checkPurity(sc, sd->postblit);
checkSafety(sc, sd->postblit);
checkNogc(sc, sd->postblit);
//checkAccess(sd, loc, sc, sd->postblit); // necessary?
return false;
}
}
return false;
}
bool Expression::checkRightThis(Scope *sc)
{
if (op == TOKerror)
return true;
if (op == TOKvar && type->ty != Terror)
{
VarExp *ve = (VarExp *)this;
if (isNeedThisScope(sc, ve->var))
{
//printf("checkRightThis sc->intypeof = %d, ad = %p, func = %p, fdthis = %p\n",
// sc->intypeof, sc->getStructClassScope(), func, fdthis);
error("need `this` for `%s` of type `%s`", ve->var->toChars(), ve->var->type->toChars());
return true;
}
}
return false;
}
/*******************************
* Check whether the expression allows RMW operations, error with rmw operator diagnostic if not.
* ex is the RHS expression, or NULL if ++/-- is used (for diagnostics)
* Returns true if error occurs.
*/
bool Expression::checkReadModifyWrite(TOK rmwOp, Expression *ex)
{
//printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex->toChars() : "");
if (!type || !type->isShared())
return false;
// atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal.
switch (rmwOp)
{
case TOKplusplus:
case TOKpreplusplus:
rmwOp = TOKaddass;
break;
case TOKminusminus:
case TOKpreminusminus:
rmwOp = TOKminass;
break;
default:
break;
}
deprecation("read-modify-write operations are not allowed for shared variables. "
"Use core.atomic.atomicOp!\"%s\"(%s, %s) instead.",
Token::tochars[rmwOp], toChars(), ex ? ex->toChars() : "1");
return false;
// note: enable when deprecation becomes an error.
// return true;
}
/*****************************
* If expression can be tested for true or false,
* returns the modified expression.
* Otherwise returns ErrorExp.
*/
Expression *Expression::toBoolean(Scope *sc)
{
// Default is 'yes' - do nothing
Expression *e = this;
Type *t = type;
Type *tb = type->toBasetype();
Type *att = NULL;
Lagain:
// Structs can be converted to bool using opCast(bool)()
if (tb->ty == Tstruct)
{
AggregateDeclaration *ad = ((TypeStruct *)tb)->sym;
/* Don't really need to check for opCast first, but by doing so we
* get better error messages if it isn't there.
*/
Dsymbol *fd = search_function(ad, Id::_cast);
if (fd)
{
e = new CastExp(loc, e, Type::tbool);
e = expressionSemantic(e, sc);
return e;
}
// Forward to aliasthis.
if (ad->aliasthis && tb != att)
{
if (!att && tb->checkAliasThisRec())
att = tb;
e = resolveAliasThis(sc, e);
t = e->type;
tb = e->type->toBasetype();
goto Lagain;
}
}
if (!t->isBoolean())
{
if (tb != Type::terror)
error("expression %s of type %s does not have a boolean value", toChars(), t->toChars());
return new ErrorExp();
}
return e;
}
/******************************
* Take address of expression.
*/
Expression *Expression::addressOf()
{
//printf("Expression::addressOf()\n");
Expression *e = new AddrExp(loc, this);
e->type = type->pointerTo();
return e;
}
/******************************
* If this is a reference, dereference it.
*/
Expression *Expression::deref()
{
//printf("Expression::deref()\n");
// type could be null if forward referencing an 'auto' variable
if (type && type->ty == Treference)
{
Expression *e = new PtrExp(loc, this);
e->type = ((TypeReference *)type)->next;
return e;
}
return this;
}
/********************************
* Does this expression statically evaluate to a boolean 'result' (true or false)?
*/
bool Expression::isBool(bool)
{
return false;
}
IntegerExp *Expression::isIntegerExp()
{
return op == TOKint64 ? (IntegerExp *)this : NULL;
}
ErrorExp *Expression::isErrorExp()
{
return op == TOKerror ? (ErrorExp *)this : NULL;
}
VoidInitExp *Expression::isVoidInitExp()
{
return op == TOKvoid ? (VoidInitExp *)this : NULL;
}
RealExp *Expression::isRealExp()
{
return op == TOKfloat64 ? (RealExp *)this : NULL;
}
ComplexExp *Expression::isComplexExp()
{
return op == TOKcomplex80 ? (ComplexExp *)this : NULL;
}
IdentifierExp *Expression::isIdentifierExp()
{
return op == TOKidentifier ? (IdentifierExp *)this : NULL;
}
DollarExp *Expression::isDollarExp()
{
return op == TOKdollar ? (DollarExp *)this : NULL;
}
DsymbolExp *Expression::isDsymbolExp()
{
return op == TOKdsymbol ? (DsymbolExp *)this : NULL;
}
ThisExp *Expression::isThisExp()
{
return op == TOKthis ? (ThisExp *)this : NULL;
}
SuperExp *Expression::isSuperExp()
{
return op == TOKsuper ? (SuperExp *)this : NULL;
}
NullExp *Expression::isNullExp()
{
return op == TOKnull ? (NullExp *)this : NULL;
}
StringExp *Expression::isStringExp()
{
return op == TOKstring ? (StringExp *)this : NULL;
}
TupleExp *Expression::isTupleExp()
{
return op == TOKtuple ? (TupleExp *)this : NULL;
}
ArrayLiteralExp *Expression::isArrayLiteralExp()
{
return op == TOKarrayliteral ? (ArrayLiteralExp *)this : NULL;
}
AssocArrayLiteralExp *Expression::isAssocArrayLiteralExp()
{
return op == TOKassocarrayliteral ? (AssocArrayLiteralExp *)this : NULL;
}
StructLiteralExp *Expression::isStructLiteralExp()
{
return op == TOKstructliteral ? (StructLiteralExp *)this : NULL;
}
TypeExp *Expression::isTypeExp()
{
return op == TOKtype ? (TypeExp *)this : NULL;
}
ScopeExp *Expression::isScopeExp()
{
return op == TOKscope ? (ScopeExp *)this : NULL;
}
TemplateExp *Expression::isTemplateExp()
{
return op == TOKtemplate ? (TemplateExp *)this : NULL;
}
NewExp *Expression::isNewExp()
{
return op == TOKnew ? (NewExp *)this : NULL;
}
NewAnonClassExp *Expression::isNewAnonClassExp()
{
return op == TOKnewanonclass ? (NewAnonClassExp *)this : NULL;
}
SymOffExp *Expression::isSymOffExp()
{
return op == TOKsymoff ? (SymOffExp *)this : NULL;
}
VarExp *Expression::isVarExp()
{
return op == TOKvar ? (VarExp *)this : NULL;
}
OverExp *Expression::isOverExp()
{
return op == TOKoverloadset ? (OverExp *)this : NULL;
}
FuncExp *Expression::isFuncExp()
{
return op == TOKfunction ? (FuncExp *)this : NULL;
}
DeclarationExp *Expression::isDeclarationExp()
{
return op == TOKdeclaration ? (DeclarationExp *)this : NULL;
}
TypeidExp *Expression::isTypeidExp()
{
return op == TOKtypeid ? (TypeidExp *)this : NULL;
}
TraitsExp *Expression::isTraitsExp()
{
return op == TOKtraits ? (TraitsExp *)this : NULL;
}
HaltExp *Expression::isHaltExp()
{
return op == TOKhalt ? (HaltExp *)this : NULL;
}
IsExp *Expression::isExp()
{
return op == TOKis ? (IsExp *)this : NULL;
}
CompileExp *Expression::isCompileExp()
{
return op == TOKmixin ? (CompileExp *)this : NULL;
}
ImportExp *Expression::isImportExp()
{
return op == TOKimport ? (ImportExp *)this : NULL;
}
AssertExp *Expression::isAssertExp()
{
return op == TOKassert ? (AssertExp *)this : NULL;
}
DotIdExp *Expression::isDotIdExp()
{
return op == TOKdotid ? (DotIdExp *)this : NULL;
}
DotTemplateExp *Expression::isDotTemplateExp()
{
return op == TOKdotti ? (DotTemplateExp *)this : NULL;
}
DotVarExp *Expression::isDotVarExp()
{
return op == TOKdotvar ? (DotVarExp *)this : NULL;
}
DotTemplateInstanceExp *Expression::isDotTemplateInstanceExp()
{
return op == TOKdotti ? (DotTemplateInstanceExp *)this : NULL;
}
DelegateExp *Expression::isDelegateExp()
{
return op == TOKdelegate ? (DelegateExp *)this : NULL;
}
DotTypeExp *Expression::isDotTypeExp()
{
return op == TOKdottype ? (DotTypeExp *)this : NULL;
}
CallExp *Expression::isCallExp()
{
return op == TOKcall ? (CallExp *)this : NULL;
}
AddrExp *Expression::isAddrExp()
{
return op == TOKaddress ? (AddrExp *)this : NULL;
}
PtrExp *Expression::isPtrExp()
{
return op == TOKstar ? (PtrExp *)this : NULL;
}
NegExp *Expression::isNegExp()
{
return op == TOKneg ? (NegExp *)this : NULL;
}
UAddExp *Expression::isUAddExp()
{
return op == TOKuadd ? (UAddExp *)this : NULL;
}
ComExp *Expression::isComExp()
{
return op == TOKtilde ? (ComExp *)this : NULL;
}
NotExp *Expression::isNotExp()
{
return op == TOKnot ? (NotExp *)this : NULL;
}
DeleteExp *Expression::isDeleteExp()
{
return op == TOKdelete ? (DeleteExp *)this : NULL;
}
CastExp *Expression::isCastExp()
{
return op == TOKcast ? (CastExp *)this : NULL;
}
VectorExp *Expression::isVectorExp()
{
return op == TOKvector ? (VectorExp *)this : NULL;
}
VectorArrayExp *Expression::isVectorArrayExp()
{
return op == TOKvectorarray ? (VectorArrayExp *)this : NULL;
}
SliceExp *Expression::isSliceExp()
{
return op == TOKslice ? (SliceExp *)this : NULL;
}
ArrayLengthExp *Expression::isArrayLengthExp()
{
return op == TOKarraylength ? (ArrayLengthExp *)this : NULL;
}
ArrayExp *Expression::isArrayExp()
{
return op == TOKarray ? (ArrayExp *)this : NULL;
}
DotExp *Expression::isDotExp()
{
return op == TOKdot ? (DotExp *)this : NULL;
}
CommaExp *Expression::isCommaExp()
{
return op == TOKcomma ? (CommaExp *)this : NULL;
}
IntervalExp *Expression::isIntervalExp()
{
return op == TOKinterval ? (IntervalExp *)this : NULL;
}
DelegatePtrExp *Expression::isDelegatePtrExp()
{
return op == TOKdelegateptr ? (DelegatePtrExp *)this : NULL;
}
DelegateFuncptrExp *Expression::isDelegateFuncptrExp()
{
return op == TOKdelegatefuncptr ? (DelegateFuncptrExp *)this : NULL;
}
IndexExp *Expression::isIndexExp()
{
return op == TOKindex ? (IndexExp *)this : NULL;
}
PostExp *Expression::isPostExp()
{
return (op == TOKplusplus || op == TOKminusminus) ? (PostExp *)this : NULL;
}
PreExp *Expression::isPreExp()
{
return (op == TOKpreplusplus || op == TOKpreminusminus) ? (PreExp *)this : NULL;
}
AssignExp *Expression::isAssignExp()
{
return op == TOKassign ? (AssignExp *)this : NULL;
}
ConstructExp *Expression::isConstructExp()
{
return op == TOKconstruct ? (ConstructExp *)this : NULL;
}
BlitExp *Expression::isBlitExp()
{
return op == TOKblit ? (BlitExp *)this : NULL;
}
AddAssignExp *Expression::isAddAssignExp()
{
return op == TOKaddass ? (AddAssignExp *)this : NULL;
}
MinAssignExp *Expression::isMinAssignExp()
{
return op == TOKminass ? (MinAssignExp *)this : NULL;
}
MulAssignExp *Expression::isMulAssignExp()
{
return op == TOKmulass ? (MulAssignExp *)this : NULL;
}
DivAssignExp *Expression::isDivAssignExp()
{
return op == TOKdivass ? (DivAssignExp *)this : NULL;
}
ModAssignExp *Expression::isModAssignExp()
{
return op == TOKmodass ? (ModAssignExp *)this : NULL;
}
AndAssignExp *Expression::isAndAssignExp()
{
return op == TOKandass ? (AndAssignExp *)this : NULL;
}
OrAssignExp *Expression::isOrAssignExp()
{
return op == TOKorass ? (OrAssignExp *)this : NULL;
}
XorAssignExp *Expression::isXorAssignExp()
{
return op == TOKxorass ? (XorAssignExp *)this : NULL;
}
PowAssignExp *Expression::isPowAssignExp()
{
return op == TOKpowass ? (PowAssignExp *)this : NULL;
}
ShlAssignExp *Expression::isShlAssignExp()
{
return op == TOKshlass ? (ShlAssignExp *)this : NULL;
}
ShrAssignExp *Expression::isShrAssignExp()
{
return op == TOKshrass ? (ShrAssignExp *)this : NULL;
}
UshrAssignExp *Expression::isUshrAssignExp()
{
return op == TOKushrass ? (UshrAssignExp *)this : NULL;
}
CatAssignExp *Expression::isCatAssignExp()
{
return op == TOKcatass ? (CatAssignExp *)this : NULL;
}
AddExp *Expression::isAddExp()
{
return op == TOKadd ? (AddExp *)this : NULL;
}
MinExp *Expression::isMinExp()
{
return op == TOKmin ? (MinExp *)this : NULL;
}
CatExp *Expression::isCatExp()
{
return op == TOKcat ? (CatExp *)this : NULL;
}
MulExp *Expression::isMulExp()
{
return op == TOKmul ? (MulExp *)this : NULL;
}
DivExp *Expression::isDivExp()
{
return op == TOKdiv ? (DivExp *)this : NULL;
}
ModExp *Expression::isModExp()
{
return op == TOKmod ? (ModExp *)this : NULL;
}
PowExp *Expression::isPowExp()
{
return op == TOKpow ? (PowExp *)this : NULL;
}
ShlExp *Expression::isShlExp()
{
return op == TOKshl ? (ShlExp *)this : NULL;
}
ShrExp *Expression::isShrExp()
{
return op == TOKshr ? (ShrExp *)this : NULL;
}
UshrExp *Expression::isUshrExp()
{
return op == TOKushr ? (UshrExp *)this : NULL;
}
AndExp *Expression::isAndExp()
{
return op == TOKand ? (AndExp *)this : NULL;
}
OrExp *Expression::isOrExp()
{
return op == TOKor ? (OrExp *)this : NULL;
}
XorExp *Expression::isXorExp()
{
return op == TOKxor ? (XorExp *)this : NULL;
}
LogicalExp *Expression::isLogicalExp()
{
return (op == TOKandand || op == TOKoror) ? (LogicalExp *)this : NULL;
}
InExp *Expression::isInExp()
{
return op == TOKin ? (InExp *)this : NULL;
}
RemoveExp *Expression::isRemoveExp()
{
return op == TOKremove ? (RemoveExp *)this : NULL;
}
EqualExp *Expression::isEqualExp()
{
return (op == TOKequal || op == TOKnotequal) ? (EqualExp *)this : NULL;
}
IdentityExp *Expression::isIdentityExp()
{
return (op == TOKidentity || op == TOKnotidentity) ? (IdentityExp *)this : NULL;
}
CondExp *Expression::isCondExp()
{
return op == TOKquestion ? (CondExp *)this : NULL;
}
DefaultInitExp *Expression::isDefaultInitExp()
{
return op == TOKdefault ? (DefaultInitExp *)this : NULL;
}
FileInitExp *Expression::isFileInitExp()
{
return (op == TOKfile || op == TOKfilefullpath) ? (FileInitExp *)this : NULL;
}
LineInitExp *Expression::isLineInitExp()
{
return op == TOKline ? (LineInitExp *)this : NULL;
}
ModuleInitExp *Expression::isModuleInitExp()
{
return op == TOKmodulestring ? (ModuleInitExp *)this : NULL;
}
FuncInitExp *Expression::isFuncInitExp()
{
return op == TOKfuncstring ? (FuncInitExp *)this : NULL;
}
PrettyFuncInitExp *Expression::isPrettyFuncInitExp()
{
return op == TOKprettyfunc ? (PrettyFuncInitExp *)this : NULL;
}
ClassReferenceExp *Expression::isClassReferenceExp()
{
return op == TOKclassreference ? (ClassReferenceExp *)this : NULL;
}
/****************************************
* Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE__FULL_PATH__ to loc.
*/
Expression *Expression::resolveLoc(Loc, Scope *)
{
return this;
}
Expressions *Expression::arraySyntaxCopy(Expressions *exps)
{
Expressions *a = NULL;
if (exps)
{
a = new Expressions();
a->setDim(exps->length);
for (size_t i = 0; i < a->length; i++)
{
Expression *e = (*exps)[i];
(*a)[i] = e ? e->syntaxCopy() : NULL;
}
}
return a;
}
/************************************************
* Destructors are attached to VarDeclarations.
* Hence, if expression returns a temp that needs a destructor,
* make sure and create a VarDeclaration for that temp.
*/
Expression *Expression::addDtorHook(Scope *)
{
return this;
}
/******************************** IntegerExp **************************/
IntegerExp::IntegerExp(Loc loc, dinteger_t value, Type *type)
: Expression(loc, TOKint64, sizeof(IntegerExp))
{
//printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type->toChars() : "");
assert(type);
if (!type->isscalar())
{
//printf("%s, loc = %d\n", toChars(), loc.linnum);
if (type->ty != Terror)
error("integral constant must be scalar type, not %s", type->toChars());
type = Type::terror;
}
this->type = type;
setInteger(value);
}
IntegerExp::IntegerExp(dinteger_t value)
: Expression(Loc(), TOKint64, sizeof(IntegerExp))
{
this->type = Type::tint32;
this->value = (d_int32) value;
}
IntegerExp *IntegerExp::create(Loc loc, dinteger_t value, Type *type)
{
return new IntegerExp(loc, value, type);
}
bool IntegerExp::equals(RootObject *o)
{
if (this == o)
return true;
if (((Expression *)o)->op == TOKint64)
{
IntegerExp *ne = (IntegerExp *)o;
if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
value == ne->value)
{
return true;
}
}
return false;
}
void IntegerExp::setInteger(dinteger_t value)
{
this->value = value;
normalize();
}
void IntegerExp::normalize()
{
/* 'Normalize' the value of the integer to be in range of the type
*/
switch (type->toBasetype()->ty)
{
case Tbool: value = (value != 0); break;
case Tint8: value = (d_int8) value; break;
case Tchar:
case Tuns8: value = (d_uns8) value; break;
case Tint16: value = (d_int16) value; break;
case Twchar:
case Tuns16: value = (d_uns16) value; break;
case Tint32: value = (d_int32) value; break;
case Tdchar:
case Tuns32: value = (d_uns32) value; break;
case Tint64: value = (d_int64) value; break;
case Tuns64: value = (d_uns64) value; break;
case Tpointer:
if (target.ptrsize == 8)
value = (d_uns64) value;
else if (target.ptrsize == 4)
value = (d_uns32) value;
else if (target.ptrsize == 2)
value = (d_uns16) value;
else
assert(0);
break;
default:
break;
}
}
dinteger_t IntegerExp::toInteger()
{
normalize(); // necessary until we fix all the paints of 'type'
return value;
}
real_t IntegerExp::toReal()
{
normalize(); // necessary until we fix all the paints of 'type'
Type *t = type->toBasetype();
if (t->ty == Tuns64)
return ldouble((d_uns64)value);
else
return ldouble((d_int64)value);
}
real_t IntegerExp::toImaginary()
{
return CTFloat::zero;
}
complex_t IntegerExp::toComplex()
{
return (complex_t)toReal();
}
bool IntegerExp::isBool(bool result)
{
bool r = toInteger() != 0;
return result ? r : !r;
}
Expression *IntegerExp::toLvalue(Scope *, Expression *e)
{
if (!e)
e = this;
else if (!loc.filename)
loc = e->loc;
e->error("constant %s is not an lvalue", e->toChars());
return new ErrorExp();
}
/******************************** ErrorExp **************************/
/* Use this expression for error recovery.
* It should behave as a 'sink' to prevent further cascaded error messages.
*/
ErrorExp::ErrorExp()
: Expression(Loc(), TOKerror, sizeof(ErrorExp))
{
type = Type::terror;
}
Expression *ErrorExp::toLvalue(Scope *, Expression *)
{
return this;
}
/******************************** RealExp **************************/
RealExp::RealExp(Loc loc, real_t value, Type *type)
: Expression(loc, TOKfloat64, sizeof(RealExp))
{
//printf("RealExp::RealExp(%Lg)\n", value);
this->value = value;
this->type = type;
}
RealExp *RealExp::create(Loc loc, real_t value, Type *type)
{
return new RealExp(loc, value,type);
}
dinteger_t RealExp::toInteger()
{
return (sinteger_t) toReal();
}
uinteger_t RealExp::toUInteger()
{
return (uinteger_t) toReal();
}
real_t RealExp::toReal()
{
return type->isreal() ? value : CTFloat::zero;
}
real_t RealExp::toImaginary()
{
return type->isreal() ? CTFloat::zero : value;
}
complex_t RealExp::toComplex()
{
return complex_t(toReal(), toImaginary());
}
/********************************
* Test to see if two reals are the same.
* Regard NaN's as equivalent.
* Regard +0 and -0 as different.
*/
int RealEquals(real_t x1, real_t x2)
{
return (CTFloat::isNaN(x1) && CTFloat::isNaN(x2)) ||
CTFloat::isIdentical(x1, x2);
}
bool RealExp::equals(RootObject *o)
{
if (this == o)
return true;
if (((Expression *)o)->op == TOKfloat64)
{
RealExp *ne = (RealExp *)o;
if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
RealEquals(value, ne->value))
{
return true;
}
}
return false;
}
bool RealExp::isBool(bool result)
{
return result ? (bool)value : !(bool)value;
}
/******************************** ComplexExp **************************/
ComplexExp::ComplexExp(Loc loc, complex_t value, Type *type)
: Expression(loc, TOKcomplex80, sizeof(ComplexExp)), value(value)
{
this->type = type;
//printf("ComplexExp::ComplexExp(%s)\n", toChars());
}
ComplexExp *ComplexExp::create(Loc loc, complex_t value, Type *type)
{
return new ComplexExp(loc, value, type);
}
dinteger_t ComplexExp::toInteger()
{
return (sinteger_t) toReal();
}
uinteger_t ComplexExp::toUInteger()
{
return (uinteger_t) toReal();
}
real_t ComplexExp::toReal()
{
return creall(value);
}
real_t ComplexExp::toImaginary()
{
return cimagl(value);
}
complex_t ComplexExp::toComplex()
{
return value;
}
bool ComplexExp::equals(RootObject *o)
{
if (this == o)
return true;
if (((Expression *)o)->op == TOKcomplex80)
{
ComplexExp *ne = (ComplexExp *)o;
if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
RealEquals(creall(value), creall(ne->value)) &&
RealEquals(cimagl(value), cimagl(ne->value)))
{
return true;
}
}
return false;
}
bool ComplexExp::isBool(bool result)
{
if (result)
return (bool)(value);
else
return !value;
}
/******************************** IdentifierExp **************************/
IdentifierExp::IdentifierExp(Loc loc, Identifier *ident)
: Expression(loc, TOKidentifier, sizeof(IdentifierExp))
{
this->ident = ident;
}
IdentifierExp *IdentifierExp::create(Loc loc, Identifier *ident)
{
return new IdentifierExp(loc, ident);
}
bool IdentifierExp::isLvalue()
{
return true;
}
Expression *IdentifierExp::toLvalue(Scope *, Expression *)
{
return this;
}
/******************************** DollarExp **************************/
DollarExp::DollarExp(Loc loc)
: IdentifierExp(loc, Id::dollar)
{
}
/******************************** DsymbolExp **************************/
DsymbolExp::DsymbolExp(Loc loc, Dsymbol *s, bool hasOverloads)
: Expression(loc, TOKdsymbol, sizeof(DsymbolExp))
{
this->s = s;
this->hasOverloads = hasOverloads;
}
/****************************************
* Resolve a symbol `s` and wraps it in an expression object.
* Params:
* hasOverloads = works if the aliased symbol is a function.
* true: it's overloaded and will be resolved later.
* false: it's exact function symbol.
*/
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads)
{
Lagain:
Expression *e;
//printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
//printf("s = '%s', s->kind = '%s'\n", s->toChars(), s->kind());
Dsymbol *olds = s;
Declaration *d = s->isDeclaration();
if (d && (d->storage_class & STCtemplateparameter))
{
s = s->toAlias();
}
else
{
if (!s->isFuncDeclaration()) // functions are checked after overloading
{
s->checkDeprecated(loc, sc);
if (d)
d->checkDisabled(loc, sc);
}
// Bugzilla 12023: if 's' is a tuple variable, the tuple is returned.
s = s->toAlias();
//printf("s = '%s', s->kind = '%s', s->needThis() = %p\n", s->toChars(), s->kind(), s->needThis());
if (s != olds && !s->isFuncDeclaration())
{
s->checkDeprecated(loc, sc);
if (d)
d->checkDisabled(loc, sc);
}
}
if (EnumMember *em = s->isEnumMember())
{
return em->getVarExp(loc, sc);
}
if (VarDeclaration *v = s->isVarDeclaration())
{
//printf("Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
if (!v->type || // during variable type inference
(!v->type->deco && v->inuse)) // during variable type semantic
{
if (v->inuse) // variable type depends on the variable itself
::error(loc, "circular reference to %s `%s`", v->kind(), v->toPrettyChars());
else // variable type cannot be determined
::error(loc, "forward reference to %s `%s`", v->kind(), v->toPrettyChars());
return new ErrorExp();
}
if (v->type->ty == Terror)
return new ErrorExp();
if ((v->storage_class & STCmanifest) && v->_init)
{
if (v->inuse)
{
::error(loc, "circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
return new ErrorExp();
}
e = v->expandInitializer(loc);
v->inuse++;
e = expressionSemantic(e, sc);
v->inuse--;
return e;
}
// Change the ancestor lambdas to delegate before hasThis(sc) call.
if (v->checkNestedReference(sc, loc))
return new ErrorExp();
if (v->needThis() && hasThis(sc))
e = new DotVarExp(loc, new ThisExp(loc), v);
else
e = new VarExp(loc, v);
e = expressionSemantic(e, sc);
return e;
}
if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
{
//printf("'%s' is a function literal\n", fld->toChars());
e = new FuncExp(loc, fld);
return expressionSemantic(e, sc);
}
if (FuncDeclaration *f = s->isFuncDeclaration())
{
f = f->toAliasFunc();
if (!f->functionSemantic())
return new ErrorExp();
if (!hasOverloads && f->checkForwardRef(loc))
return new ErrorExp();
FuncDeclaration *fd = s->isFuncDeclaration();
fd->type = f->type;
return new VarExp(loc, fd, hasOverloads);
}
if (OverDeclaration *od = s->isOverDeclaration())
{
e = new VarExp(loc, od, true);
e->type = Type::tvoid;
return e;
}
if (OverloadSet *o = s->isOverloadSet())
{
//printf("'%s' is an overload set\n", o->toChars());
return new OverExp(loc, o);
}
if (Import *imp = s->isImport())
{
if (!imp->pkg)
{
::error(loc, "forward reference of import %s", imp->toChars());
return new ErrorExp();
}
ScopeExp *ie = new ScopeExp(loc, imp->pkg);
return expressionSemantic(ie, sc);
}
if (Package *pkg = s->isPackage())
{
ScopeExp *ie = new ScopeExp(loc, pkg);
return expressionSemantic(ie, sc);
}
if (Module *mod = s->isModule())
{
ScopeExp *ie = new ScopeExp(loc, mod);
return expressionSemantic(ie, sc);
}
if (Nspace *ns = s->isNspace())
{
ScopeExp *ie = new ScopeExp(loc, ns);
return expressionSemantic(ie, sc);
}
if (Type *t = s->getType())
{
return expressionSemantic(new TypeExp(loc, t), sc);
}
if (TupleDeclaration *tup = s->isTupleDeclaration())
{
if (tup->needThis() && hasThis(sc))
e = new DotVarExp(loc, new ThisExp(loc), tup);
else
e = new TupleExp(loc, tup);
e = expressionSemantic(e, sc);
return e;
}
if (TemplateInstance *ti = s->isTemplateInstance())
{
dsymbolSemantic(ti, sc);
if (!ti->inst || ti->errors)
return new ErrorExp();
s = ti->toAlias();
if (!s->isTemplateInstance())
goto Lagain;
e = new ScopeExp(loc, ti);
e = expressionSemantic(e, sc);
return e;
}
if (TemplateDeclaration *td = s->isTemplateDeclaration())
{
Dsymbol *p = td->toParent2();
FuncDeclaration *fdthis = hasThis(sc);
AggregateDeclaration *ad = p ? p->isAggregateDeclaration() : NULL;
if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad &&
(td->_scope->stc & STCstatic) == 0)
{
e = new DotTemplateExp(loc, new ThisExp(loc), td);
}
else
e = new TemplateExp(loc, td);
e = expressionSemantic(e, sc);
return e;
}
::error(loc, "%s `%s` is not a variable", s->kind(), s->toChars());
return new ErrorExp();
}
bool DsymbolExp::isLvalue()
{
return true;
}
Expression *DsymbolExp::toLvalue(Scope *, Expression *)
{
return this;
}
/******************************** ThisExp **************************/
ThisExp::ThisExp(Loc loc)
: Expression(loc, TOKthis, sizeof(ThisExp))
{
//printf("ThisExp::ThisExp() loc = %d\n", loc.linnum);
var = NULL;
}
bool ThisExp::isBool(bool result)
{
return result ? true : false;
}
bool ThisExp::isLvalue()
{
// Class `this` should be an rvalue; struct `this` should be an lvalue.
return type->toBasetype()->ty != Tclass;
}
Expression *ThisExp::toLvalue(Scope *sc, Expression *e)
{
if (type->toBasetype()->ty == Tclass)
{
// Class `this` is an rvalue; struct `this` is an lvalue.
return Expression::toLvalue(sc, e);
}
return this;
}
/******************************** SuperExp **************************/
SuperExp::SuperExp(Loc loc)
: ThisExp(loc)
{
op = TOKsuper;
}
/******************************** NullExp **************************/
NullExp::NullExp(Loc loc, Type *type)
: Expression(loc, TOKnull, sizeof(NullExp))
{
committed = 0;
this->type = type;
}
bool NullExp::equals(RootObject *o)
{
if (o && o->dyncast() == DYNCAST_EXPRESSION)
{
Expression *e = (Expression *)o;
if (e->op == TOKnull &&
type->equals(e->type))
{
return true;
}
}
return false;
}
bool NullExp::isBool(bool result)
{
return result ? false : true;
}
StringExp *NullExp::toStringExp()
{
if (implicitConvTo(Type::tstring))
{
StringExp *se = new StringExp(loc, (char*)mem.xcalloc(1, 1), 0);
se->type = Type::tstring;
return se;
}
return NULL;
}
/******************************** StringExp **************************/
StringExp::StringExp(Loc loc, char *string)
: Expression(loc, TOKstring, sizeof(StringExp))
{
this->string = string;
this->len = strlen(string);
this->sz = 1;
this->committed = 0;
this->postfix = 0;
this->ownedByCtfe = OWNEDcode;
}
StringExp::StringExp(Loc loc, void *string, size_t len)
: Expression(loc, TOKstring, sizeof(StringExp))
{
this->string = string;
this->len = len;
this->sz = 1;
this->committed = 0;
this->postfix = 0;
this->ownedByCtfe = OWNEDcode;
}
StringExp::StringExp(Loc loc, void *string, size_t len, utf8_t postfix)
: Expression(loc, TOKstring, sizeof(StringExp))
{
this->string = string;
this->len = len;
this->sz = 1;
this->committed = 0;
this->postfix = postfix;
this->ownedByCtfe = OWNEDcode;
}
StringExp *StringExp::create(Loc loc, char *s)
{
return new StringExp(loc, s);
}
StringExp *StringExp::create(Loc loc, void *string, size_t len)
{
return new StringExp(loc, string, len);
}
bool StringExp::equals(RootObject *o)
{
//printf("StringExp::equals('%s') %s\n", o->toChars(), toChars());
if (o && o->dyncast() == DYNCAST_EXPRESSION)
{
Expression *e = (Expression *)o;
if (e->op == TOKstring)
{
return compare(o) == 0;
}
}
return false;
}
/**********************************
* Return the number of code units the string would be if it were re-encoded
* as tynto.
* Params:
* tynto = code unit type of the target encoding
* Returns:
* number of code units
*/
size_t StringExp::numberOfCodeUnits(int tynto) const
{
int encSize;
switch (tynto)
{
case 0: return len;
case Tchar: encSize = 1; break;
case Twchar: encSize = 2; break;
case Tdchar: encSize = 4; break;
default:
assert(0);
}
if (sz == encSize)
return len;
size_t result = 0;
dchar_t c;
switch (sz)
{
case 1:
for (size_t u = 0; u < len;)
{
if (const char *p = utf_decodeChar((utf8_t *)string, len, &u, &c))
{
error("%s", p);
return 0;
}
result += utf_codeLength(encSize, c);
}
break;
case 2:
for (size_t u = 0; u < len;)
{
if (const char *p = utf_decodeWchar((utf16_t *)string, len, &u, &c))
{
error("%s", p);
return 0;
}
result += utf_codeLength(encSize, c);
}
break;
case 4:
for (size_t u = 0; u < len;)
{
c = *((utf32_t *)((char *)string + u));
u += 4;
result += utf_codeLength(encSize, c);
}
break;
default:
assert(0);
}
return result;
}
/**********************************************
* Write the contents of the string to dest.
* Use numberOfCodeUnits() to determine size of result.
* Params:
* dest = destination
* tyto = encoding type of the result
* zero = add terminating 0
*/
void StringExp::writeTo(void *dest, bool zero, int tyto) const
{
int encSize;
switch (tyto)
{
case 0: encSize = sz; break;
case Tchar: encSize = 1; break;
case Twchar: encSize = 2; break;
case Tdchar: encSize = 4; break;
default:
assert(0);
}
if (sz == encSize)
{
memcpy(dest, string, len * sz);
if (zero)
memset((char *)dest + len * sz, 0, sz);
}
else
assert(0);
}
/**************************************************
* If the string data is UTF-8 and can be accessed directly,
* return a pointer to it.
* Do not assume a terminating 0.
* Returns:
* pointer to string data if possible, null if not
*/
char *StringExp::toPtr()
{
return (sz == 1) ? (char*)string : NULL;
}
StringExp *StringExp::toStringExp()
{
return this;
}
/****************************************
* Convert string to char[].
*/
StringExp *StringExp::toUTF8(Scope *sc)
{
if (sz != 1)
{ // Convert to UTF-8 string
committed = 0;
Expression *e = castTo(sc, Type::tchar->arrayOf());
e = e->optimize(WANTvalue);
assert(e->op == TOKstring);
StringExp *se = (StringExp *)e;
assert(se->sz == 1);
return se;
}
return this;
}
int StringExp::compare(RootObject *obj)
{
//printf("StringExp::compare()\n");
// Used to sort case statement expressions so we can do an efficient lookup
StringExp *se2 = (StringExp *)(obj);
// This is a kludge so isExpression() in template.c will return 5
// for StringExp's.
if (!se2)
return 5;
assert(se2->op == TOKstring);
size_t len1 = len;
size_t len2 = se2->len;
//printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2);
if (len1 == len2)
{
switch (sz)
{
case 1:
return memcmp((char *)string, (char *)se2->string, len1);
case 2:
{
d_uns16 *s1 = (d_uns16 *)string;
d_uns16 *s2 = (d_uns16 *)se2->string;
for (size_t u = 0; u < len; u++)
{
if (s1[u] != s2[u])
return s1[u] - s2[u];
}
}
break;
case 4:
{
d_uns32 *s1 = (d_uns32 *)string;
d_uns32 *s2 = (d_uns32 *)se2->string;
for (size_t u = 0; u < len; u++)
{
if (s1[u] != s2[u])
return s1[u] - s2[u];
}
}
break;
default:
assert(0);
}
}
return (int)(len1 - len2);
}
bool StringExp::isBool(bool result)
{
return result ? true : false;
}
bool StringExp::isLvalue()
{
/* string literal is rvalue in default, but
* conversion to reference of static array is only allowed.
*/
return (type && type->toBasetype()->ty == Tsarray);
}
Expression *StringExp::toLvalue(Scope *sc, Expression *e)
{
//printf("StringExp::toLvalue(%s) type = %s\n", toChars(), type ? type->toChars() : NULL);
return (type && type->toBasetype()->ty == Tsarray)
? this : Expression::toLvalue(sc, e);
}
Expression *StringExp::modifiableLvalue(Scope *, Expression *)
{
error("cannot modify string literal %s", toChars());
return new ErrorExp();
}
unsigned StringExp::charAt(uinteger_t i) const
{ unsigned value;
switch (sz)
{
case 1:
value = ((utf8_t *)string)[(size_t)i];
break;
case 2:
value = ((unsigned short *)string)[(size_t)i];
break;
case 4:
value = ((unsigned int *)string)[(size_t)i];
break;
default:
assert(0);
break;
}
return value;
}
/************************ ArrayLiteralExp ************************************/
// [ e1, e2, e3, ... ]
ArrayLiteralExp::ArrayLiteralExp(Loc loc, Type *type, Expressions *elements)
: Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
{
this->basis = NULL;
this->type = type;
this->elements = elements;
this->ownedByCtfe = OWNEDcode;
}
ArrayLiteralExp::ArrayLiteralExp(Loc loc, Type *type, Expression *e)
: Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
{
this->basis = NULL;
this->type = type;
elements = new Expressions;
elements->push(e);
this->ownedByCtfe = OWNEDcode;
}
ArrayLiteralExp::ArrayLiteralExp(Loc loc, Type *type, Expression *basis, Expressions *elements)
: Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
{
this->basis = basis;
this->type = type;
this->elements = elements;
this->ownedByCtfe = OWNEDcode;
}
ArrayLiteralExp *ArrayLiteralExp::create(Loc loc, Expressions *elements)
{
return new ArrayLiteralExp(loc, NULL, elements);
}
bool ArrayLiteralExp::equals(RootObject *o)
{
if (this == o)
return true;
if (o && o->dyncast() == DYNCAST_EXPRESSION &&
((Expression *)o)->op == TOKarrayliteral)
{
ArrayLiteralExp *ae = (ArrayLiteralExp *)o;
if (elements->length != ae->elements->length)
return false;
if (elements->length == 0 &&
!type->equals(ae->type))
{
return false;
}
for (size_t i = 0; i < elements->length; i++)
{
Expression *e1 = (*elements)[i];
Expression *e2 = (*ae->elements)[i];
if (!e1)
e1 = basis;
if (!e2)
e2 = basis;
if (e1 != e2 &&
(!e1 || !e2 || !e1->equals(e2)))
return false;
}
return true;
}
return false;
}
Expression *ArrayLiteralExp::syntaxCopy()
{
return new ArrayLiteralExp(loc,
NULL,
basis ? basis->syntaxCopy() : NULL,
arraySyntaxCopy(elements));
}
Expression *ArrayLiteralExp::getElement(size_t i)
{
Expression *el = (*elements)[i];
if (!el)
el = basis;
return el;
}
static void appendArrayLiteral(Expressions *elems, ArrayLiteralExp *ale)
{
if (!ale->elements)
return;
size_t d = elems->length;
elems->append(ale->elements);
for (size_t i = d; i < elems->length; i++)
{
Expression *el = (*elems)[i];
if (!el)
(*elems)[i] = ale->basis;
}
}
/* Copy element `Expressions` in the parameters when they're `ArrayLiteralExp`s.
* Params:
* e1 = If it's ArrayLiteralExp, its `elements` will be copied.
* Otherwise, `e1` itself will be pushed into the new `Expressions`.
* e2 = If it's not `null`, it will be pushed/appended to the new
* `Expressions` by the same way with `e1`.
* Returns:
* Newly allocated `Expressions`. Note that it points to the original
* `Expression` values in e1 and e2.
*/
Expressions* ArrayLiteralExp::copyElements(Expression *e1, Expression *e2)
{
Expressions *elems = new Expressions();
if (e1->op == TOKarrayliteral)
appendArrayLiteral(elems, (ArrayLiteralExp *)e1);
else
elems->push(e1);
if (e2)
{
if (e2->op == TOKarrayliteral)
appendArrayLiteral(elems, (ArrayLiteralExp *)e2);
else
elems->push(e2);
}
return elems;
}
bool ArrayLiteralExp::isBool(bool result)
{
size_t dim = elements ? elements->length : 0;
return result ? (dim != 0) : (dim == 0);
}
StringExp *ArrayLiteralExp::toStringExp()
{
TY telem = type->nextOf()->toBasetype()->ty;
if (telem == Tchar || telem == Twchar || telem == Tdchar ||
(telem == Tvoid && (!elements || elements->length == 0)))
{
unsigned char sz = 1;
if (telem == Twchar) sz = 2;
else if (telem == Tdchar) sz = 4;
OutBuffer buf;
if (elements)
{
for (size_t i = 0; i < elements->length; ++i)
{
Expression *ch = getElement(i);
if (ch->op != TOKint64)
return NULL;
if (sz == 1)
buf.writeByte((unsigned)ch->toInteger());
else if (sz == 2)
buf.writeword((unsigned)ch->toInteger());
else
buf.write4((unsigned)ch->toInteger());
}
}
char prefix;
if (sz == 1) { prefix = 'c'; buf.writeByte(0); }
else if (sz == 2) { prefix = 'w'; buf.writeword(0); }
else { prefix = 'd'; buf.write4(0); }
const size_t len = buf.length() / sz - 1;
StringExp *se = new StringExp(loc, buf.extractData(), len, prefix);
se->sz = sz;
se->type = type;
return se;
}
return NULL;
}
/************************ AssocArrayLiteralExp ************************************/
// [ key0 : value0, key1 : value1, ... ]
AssocArrayLiteralExp::AssocArrayLiteralExp(Loc loc,
Expressions *keys, Expressions *values)
: Expression(loc, TOKassocarrayliteral, sizeof(AssocArrayLiteralExp))
{
assert(keys->length == values->length);
this->keys = keys;
this->values = values;
this->ownedByCtfe = OWNEDcode;
}
bool AssocArrayLiteralExp::equals(RootObject *o)
{
if (this == o)
return true;
if (o && o->dyncast() == DYNCAST_EXPRESSION &&
((Expression *)o)->op == TOKassocarrayliteral)
{
AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)o;
if (keys->length != ae->keys->length)
return false;
size_t count = 0;
for (size_t i = 0; i < keys->length; i++)
{
for (size_t j = 0; j < ae->keys->length; j++)
{
if ((*keys)[i]->equals((*ae->keys)[j]))
{
if (!(*values)[i]->equals((*ae->values)[j]))
return false;
++count;
}
}
}
return count == keys->length;
}
return false;
}
Expression *AssocArrayLiteralExp::syntaxCopy()
{
return new AssocArrayLiteralExp(loc,
arraySyntaxCopy(keys), arraySyntaxCopy(values));
}
bool AssocArrayLiteralExp::isBool(bool result)
{
size_t dim = keys->length;
return result ? (dim != 0) : (dim == 0);
}
/************************ StructLiteralExp ************************************/
// sd( e1, e2, e3, ... )
StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype)
: Expression(loc, TOKstructliteral, sizeof(StructLiteralExp))
{
this->sd = sd;
if (!elements)
elements = new Expressions();
this->elements = elements;
this->stype = stype;
this->useStaticInit = false;
this->sym = NULL;
this->ownedByCtfe = OWNEDcode;
this->origin = this;
this->stageflags = 0;
this->inlinecopy = NULL;
//printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
}
StructLiteralExp *StructLiteralExp::create(Loc loc, StructDeclaration *sd, void *elements, Type *stype)
{
return new StructLiteralExp(loc, sd, (Expressions *)elements, stype);
}
bool StructLiteralExp::equals(RootObject *o)
{
if (this == o)
return true;
if (o && o->dyncast() == DYNCAST_EXPRESSION &&
((Expression *)o)->op == TOKstructliteral)
{
StructLiteralExp *se = (StructLiteralExp *)o;
if (!type->equals(se->type))
return false;
if (elements->length != se->elements->length)
return false;
for (size_t i = 0; i < elements->length; i++)
{
Expression *e1 = (*elements)[i];
Expression *e2 = (*se->elements)[i];
if (e1 != e2 &&
(!e1 || !e2 || !e1->equals(e2)))
return false;
}
return true;
}
return false;
}
Expression *StructLiteralExp::syntaxCopy()
{
StructLiteralExp *exp = new StructLiteralExp(loc, sd, arraySyntaxCopy(elements), type ? type : stype);
exp->origin = this;
return exp;
}
Expression *StructLiteralExp::addDtorHook(Scope *sc)
{
/* If struct requires a destructor, rewrite as:
* (S tmp = S()),tmp
* so that the destructor can be hung on tmp.
*/
if (sd->dtor && sc->func)
{
/* Make an identifier for the temporary of the form:
* __sl%s%d, where %s is the struct name
*/
const size_t len = 10;
char buf[len + 1];
buf[len] = 0;
strcpy(buf, "__sl");
strncat(buf, sd->ident->toChars(), len - 4 - 1);
assert(buf[len] == 0);
VarDeclaration *tmp = copyToTemp(0, buf, this);
Expression *ae = new DeclarationExp(loc, tmp);
Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp));
e = expressionSemantic(e, sc);
return e;
}
return this;
}
/**************************************
* Gets expression at offset of type.
* Returns NULL if not found.
*/
Expression *StructLiteralExp::getField(Type *type, unsigned offset)
{
//printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
// /*toChars()*/"", type->toChars(), offset);
Expression *e = NULL;
int i = getFieldIndex(type, offset);
if (i != -1)
{
//printf("\ti = %d\n", i);
if (i == (int)sd->fields.length - 1 && sd->isNested())
return NULL;
assert(i < (int)elements->length);
e = (*elements)[i];
if (e)
{
//printf("e = %s, e->type = %s\n", e->toChars(), e->type->toChars());
/* If type is a static array, and e is an initializer for that array,
* then the field initializer should be an array literal of e.
*/
if (e->type->castMod(0) != type->castMod(0) && type->ty == Tsarray)
{ TypeSArray *tsa = (TypeSArray *)type;
size_t length = (size_t)tsa->dim->toInteger();
Expressions *z = new Expressions;
z->setDim(length);
for (size_t q = 0; q < length; ++q)
(*z)[q] = e->copy();
e = new ArrayLiteralExp(loc, type, z);
}
else
{
e = e->copy();
e->type = type;
}
if (useStaticInit && e->op == TOKstructliteral &&