blob: af762eb3c66f36f9eb0c65a06ffa0d293476e4b6 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2019 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);
MOD MODmerge(MOD mod1, MOD mod2);
void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod);
Expression *trySemantic(Expression *e, Scope *sc);
Expression *semantic(Expression *e, Scope *sc);
Expression *semanticX(DotIdExp *exp, Scope *sc);
Expression *semanticY(DotIdExp *exp, Scope *sc, int flag);
Expression *semanticY(DotTemplateInstanceExp *exp, Scope *sc, int flag);
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg);
/*************************************************************
* Given var, we need to get the
* right 'this' pointer if var is in an outer class, but our
* existing 'this' pointer is in an inner class.
* Input:
* e1 existing 'this'
* ad struct or class we need the correct 'this' for
* var the specific member of ad we're accessing
*/
Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad,
Expression *e1, Declaration *var, int flag = 0)
{
//printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1->toChars(), ad->toChars(), var->toChars());
L1:
Type *t = e1->type->toBasetype();
//printf("e1->type = %s, var->type = %s\n", e1->type->toChars(), var->type->toChars());
/* If e1 is not the 'this' pointer for ad
*/
if (ad &&
!(t->ty == Tpointer && t->nextOf()->ty == Tstruct &&
((TypeStruct *)t->nextOf())->sym == ad)
&&
!(t->ty == Tstruct &&
((TypeStruct *)t)->sym == ad)
)
{
ClassDeclaration *cd = ad->isClassDeclaration();
ClassDeclaration *tcd = t->isClassHandle();
/* e1 is the right this if ad is a base class of e1
*/
if (!cd || !tcd ||
!(tcd == cd || cd->isBaseOf(tcd, NULL))
)
{
/* Only classes can be inner classes with an 'outer'
* member pointing to the enclosing class instance
*/
if (tcd && tcd->isNested())
{
/* e1 is the 'this' pointer for an inner class: tcd.
* Rewrite it as the 'this' pointer for the outer class.
*/
e1 = new DotVarExp(loc, e1, tcd->vthis);
e1->type = tcd->vthis->type;
e1->type = e1->type->addMod(t->mod);
// Do not call checkNestedRef()
//e1 = semantic(e1, sc);
// Skip up over nested functions, and get the enclosing
// class type.
int n = 0;
Dsymbol *s;
for (s = tcd->toParent();
s && s->isFuncDeclaration();
s = s->toParent())
{
FuncDeclaration *f = s->isFuncDeclaration();
if (f->vthis)
{
//printf("rewriting e1 to %s's this\n", f->toChars());
n++;
e1 = new VarExp(loc, f->vthis);
}
else
{
e1->error("need 'this' of type %s to access member %s"
" from static function %s",
ad->toChars(), var->toChars(), f->toChars());
e1 = new ErrorExp();
return e1;
}
}
if (s && s->isClassDeclaration())
{
e1->type = s->isClassDeclaration()->type;
e1->type = e1->type->addMod(t->mod);
if (n > 1)
e1 = semantic(e1, sc);
}
else
e1 = semantic(e1, sc);
goto L1;
}
/* Can't find a path from e1 to ad
*/
if (flag)
return NULL;
e1->error("this for %s needs to be type %s not type %s",
var->toChars(), ad->toChars(), t->toChars());
return new ErrorExp();
}
}
return e1;
}
/*****************************************
* 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;
}
/***************************************
* Pull out any properties.
*/
Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL)
{
//printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token::toChars(e1->op), e1->toChars(), e2 ? e2->toChars() : NULL);
Loc loc = e1->loc;
OverloadSet *os;
Dsymbol *s;
Objects *tiargs;
Type *tthis;
if (e1->op == TOKdot)
{
DotExp *de = (DotExp *)e1;
if (de->e2->op == TOKoverloadset)
{
tiargs = NULL;
tthis = de->e1->type;
os = ((OverExp *)de->e2)->vars;
goto Los;
}
}
else if (e1->op == TOKoverloadset)
{
tiargs = NULL;
tthis = NULL;
os = ((OverExp *)e1)->vars;
Los:
assert(os);
FuncDeclaration *fd = NULL;
if (e2)
{
e2 = semantic(e2, sc);
if (e2->op == TOKerror)
return new ErrorExp();
e2 = resolveProperties(sc, e2);
Expressions a;
a.push(e2);
for (size_t i = 0; i < os->a.dim; i++)
{
FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, &a, 1);
if (f)
{
if (f->errors)
return new ErrorExp();
fd = f;
assert(fd->type->ty == Tfunction);
}
}
if (fd)
{
Expression *e = new CallExp(loc, e1, e2);
return semantic(e, sc);
}
}
{
for (size_t i = 0; i < os->a.dim; i++)
{
FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, NULL, 1);
if (f)
{
if (f->errors)
return new ErrorExp();
fd = f;
assert(fd->type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)fd->type;
if (!tf->isref && e2)
goto Leproplvalue;
}
}
if (fd)
{
Expression *e = new CallExp(loc, e1);
if (e2)
e = new AssignExp(loc, e, e2);
return semantic(e, sc);
}
}
if (e2)
goto Leprop;
}
else if (e1->op == TOKdotti)
{
DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1;
if (!dti->findTempDecl(sc))
goto Leprop;
if (!dti->ti->semanticTiargs(sc))
goto Leprop;
tiargs = dti->ti->tiargs;
tthis = dti->e1->type;
if ((os = dti->ti->tempdecl->isOverloadSet()) != NULL)
goto Los;
if ((s = dti->ti->tempdecl) != NULL)
goto Lfd;
}
else if (e1->op == TOKdottd)
{
DotTemplateExp *dte = (DotTemplateExp *)e1;
s = dte->td;
tiargs = NULL;
tthis = dte->e1->type;
goto Lfd;
}
else if (e1->op == TOKscope)
{
s = ((ScopeExp *)e1)->sds;
TemplateInstance *ti = s->isTemplateInstance();
if (ti && !ti->semanticRun && ti->tempdecl)
{
//assert(ti->needsTypeInference(sc));
if (!ti->semanticTiargs(sc))
goto Leprop;
tiargs = ti->tiargs;
tthis = NULL;
if ((os = ti->tempdecl->isOverloadSet()) != NULL)
goto Los;
if ((s = ti->tempdecl) != NULL)
goto Lfd;
}
}
else if (e1->op == TOKtemplate)
{
s = ((TemplateExp *)e1)->td;
tiargs = NULL;
tthis = NULL;
goto Lfd;
}
else if (e1->op == TOKdotvar && e1->type && e1->type->toBasetype()->ty == Tfunction)
{
DotVarExp *dve = (DotVarExp *)e1;
s = dve->var->isFuncDeclaration();
tiargs = NULL;
tthis = dve->e1->type;
goto Lfd;
}
else if (e1->op == TOKvar && e1->type && e1->type->toBasetype()->ty == Tfunction)
{
s = ((VarExp *)e1)->var->isFuncDeclaration();
tiargs = NULL;
tthis = NULL;
Lfd:
assert(s);
if (e2)
{
e2 = semantic(e2, sc);
if (e2->op == TOKerror)
return new ErrorExp();
e2 = resolveProperties(sc, e2);
Expressions a;
a.push(e2);
FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1);
if (fd && fd->type)
{
if (fd->errors)
return new ErrorExp();
assert(fd->type->ty == Tfunction);
Expression *e = new CallExp(loc, e1, e2);
return semantic(e, sc);
}
}
{
FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, NULL, 1);
if (fd && fd->type)
{
if (fd->errors)
return new ErrorExp();
assert(fd->type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)fd->type;
if (!e2 || tf->isref)
{
Expression *e = new CallExp(loc, e1);
if (e2)
e = new AssignExp(loc, e, e2);
return semantic(e, sc);
}
}
}
if (FuncDeclaration *fd = s->isFuncDeclaration())
{
// Keep better diagnostic message for invalid property usage of functions
assert(fd->type->ty == Tfunction);
Expression *e = new CallExp(loc, e1, e2);
return semantic(e, sc);
}
if (e2)
goto Leprop;
}
if (e1->op == TOKvar)
{
VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v && ve->checkPurity(sc, v))
return new ErrorExp();
}
if (e2)
return NULL;
if (e1->type &&
e1->op != TOKtype) // function type is not a property
{
/* Look for e1 being a lazy parameter; rewrite as delegate call
*/
if (e1->op == TOKvar)
{
VarExp *ve = (VarExp *)e1;
if (ve->var->storage_class & STClazy)
{
Expression *e = new CallExp(loc, e1);
return semantic(e, sc);
}
}
else if (e1->op == TOKdotvar)
{
// Check for reading overlapped pointer field in @safe code.
if (checkUnsafeAccess(sc, e1, true, true))
return new ErrorExp();
}
else if (e1->op == TOKdot)
{
e1->error("expression has no value");
return new ErrorExp();
}
else if (e1->op == TOKcall)
{
CallExp *ce = (CallExp *)e1;
// Check for reading overlapped pointer field in @safe code.
if (checkUnsafeAccess(sc, ce->e1, true, true))
return new ErrorExp();
}
}
if (!e1->type)
{
error(loc, "cannot resolve type for %s", e1->toChars());
e1 = new ErrorExp();
}
return e1;
Leprop:
error(loc, "not a property %s", e1->toChars());
return new ErrorExp();
Leproplvalue:
error(loc, "%s is not an lvalue", e1->toChars());
return new ErrorExp();
}
Expression *resolveProperties(Scope *sc, Expression *e)
{
//printf("resolveProperties(%s)\n", e->toChars());
e = resolvePropertiesX(sc, e);
if (e->checkRightThis(sc))
return new ErrorExp();
return e;
}
/******************************
* Check the tail CallExp is really property function call.
*/
static bool checkPropertyCall(Expression *e)
{
while (e->op == TOKcomma)
e = ((CommaExp *)e)->e2;
if (e->op == TOKcall)
{
CallExp *ce = (CallExp *)e;
TypeFunction *tf;
if (ce->f)
{
tf = (TypeFunction *)ce->f->type;
/* If a forward reference to ce->f, try to resolve it
*/
if (!tf->deco && ce->f->_scope)
{
ce->f->semantic(ce->f->_scope);
tf = (TypeFunction *)ce->f->type;
}
}
else if (ce->e1->type->ty == Tfunction)
tf = (TypeFunction *)ce->e1->type;
else if (ce->e1->type->ty == Tdelegate)
tf = (TypeFunction *)ce->e1->type->nextOf();
else if (ce->e1->type->ty == Tpointer && ce->e1->type->nextOf()->ty == Tfunction)
tf = (TypeFunction *)ce->e1->type->nextOf();
else
assert(0);
}
return false;
}
/******************************
* If e1 is a property function (template), resolve it.
*/
Expression *resolvePropertiesOnly(Scope *sc, Expression *e1)
{
//printf("e1 = %s %s\n", Token::toChars(e1->op), e1->toChars());
OverloadSet *os;
FuncDeclaration *fd;
TemplateDeclaration *td;
if (e1->op == TOKdot)
{
DotExp *de = (DotExp *)e1;
if (de->e2->op == TOKoverloadset)
{
os = ((OverExp *)de->e2)->vars;
goto Los;
}
}
else if (e1->op == TOKoverloadset)
{
os = ((OverExp *)e1)->vars;
Los:
assert(os);
for (size_t i = 0; i < os->a.dim; i++)
{
Dsymbol *s = os->a[i];
fd = s->isFuncDeclaration();
td = s->isTemplateDeclaration();
if (fd)
{
if (((TypeFunction *)fd->type)->isproperty)
return resolveProperties(sc, e1);
}
else if (td && td->onemember &&
(fd = td->onemember->isFuncDeclaration()) != NULL)
{
if (((TypeFunction *)fd->type)->isproperty ||
(fd->storage_class2 & STCproperty) ||
(td->_scope->stc & STCproperty))
{
return resolveProperties(sc, e1);
}
}
}
}
else if (e1->op == TOKdotti)
{
DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1;
if (dti->ti->tempdecl && (td = dti->ti->tempdecl->isTemplateDeclaration()) != NULL)
goto Ltd;
}
else if (e1->op == TOKdottd)
{
td = ((DotTemplateExp *)e1)->td;
goto Ltd;
}
else if (e1->op == TOKscope)
{
Dsymbol *s = ((ScopeExp *)e1)->sds;
TemplateInstance *ti = s->isTemplateInstance();
if (ti && !ti->semanticRun && ti->tempdecl)
{
if ((td = ti->tempdecl->isTemplateDeclaration()) != NULL)
goto Ltd;
}
}
else if (e1->op == TOKtemplate)
{
td = ((TemplateExp *)e1)->td;
Ltd:
assert(td);
if (td->onemember &&
(fd = td->onemember->isFuncDeclaration()) != NULL)
{
if (((TypeFunction *)fd->type)->isproperty ||
(fd->storage_class2 & STCproperty) ||
(td->_scope->stc & STCproperty))
{
return resolveProperties(sc, e1);
}
}
}
else if (e1->op == TOKdotvar && e1->type->ty == Tfunction)
{
DotVarExp *dve = (DotVarExp *)e1;
fd = dve->var->isFuncDeclaration();
goto Lfd;
}
else if (e1->op == TOKvar && e1->type->ty == Tfunction &&
(sc->intypeof || !((VarExp *)e1)->var->needThis()))
{
fd = ((VarExp *)e1)->var->isFuncDeclaration();
Lfd:
assert(fd);
if (((TypeFunction *)fd->type)->isproperty)
return resolveProperties(sc, e1);
}
return e1;
}
// TODO: merge with Scope::search::searchScopes()
static Dsymbol *searchScopes(Scope *sc, Loc loc, Identifier *ident, int flags)
{
Dsymbol *s = NULL;
for (Scope *scx = sc; scx; scx = scx->enclosing)
{
if (!scx->scopesym)
continue;
if (scx->scopesym->isModule())
flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
s = scx->scopesym->search(loc, ident, flags);
if (s)
{
// overload set contains only module scope symbols.
if (s->isOverloadSet())
break;
// selective/renamed imports also be picked up
if (AliasDeclaration *ad = s->isAliasDeclaration())
{
if (ad->_import)
break;
}
// See only module scope symbols for UFCS target.
Dsymbol *p = s->toParent2();
if (p && p->isModule())
break;
}
s = NULL;
// Stop when we hit a module, but keep going if that is not just under the global scope
if (scx->scopesym->isModule() && !(scx->enclosing && !scx->enclosing->enclosing))
break;
}
return s;
}
/******************************
* Find symbol in accordance with the UFCS name look up rule
*/
Expression *searchUFCS(Scope *sc, UnaExp *ue, Identifier *ident)
{
//printf("searchUFCS(ident = %s)\n", ident->toChars());
Loc loc = ue->loc;
int flags = 0;
Dsymbol *s = NULL;
if (sc->flags & SCOPEignoresymbolvisibility)
flags |= IgnoreSymbolVisibility;
Dsymbol *sold = NULL;
if (global.params.bug10378 || global.params.check10378)
{
sold = searchScopes(sc, loc, ident, flags | IgnoreSymbolVisibility);
if (!global.params.check10378)
{
s = sold;
goto Lsearchdone;
}
}
// First look in local scopes
s = searchScopes(sc, loc, ident, flags | SearchLocalsOnly);
if (!s)
{
// Second look in imported modules
s = searchScopes(sc, loc, ident, flags | SearchImportsOnly);
/** Still find private symbols, so that symbols that weren't access
* checked by the compiler remain usable. Once the deprecation is over,
* this should be moved to search_correct instead.
*/
if (!s && !(flags & IgnoreSymbolVisibility))
{
s = searchScopes(sc, loc, ident, flags | SearchLocalsOnly | IgnoreSymbolVisibility);
if (!s)
s = searchScopes(sc, loc, ident, flags | SearchImportsOnly | IgnoreSymbolVisibility);
if (s)
::deprecation(loc, "%s is not visible from module %s", s->toPrettyChars(), sc->_module->toChars());
}
}
if (global.params.check10378)
{
Dsymbol *snew = s;
if (sold != snew)
Scope::deprecation10378(loc, sold, snew);
if (global.params.bug10378)
s = sold;
}
Lsearchdone:
if (!s)
return ue->e1->type->Type::getProperty(loc, ident, 0);
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
TemplateDeclaration *td = getFuncTemplateDecl(f);
if (td)
{
if (td->overroot)
td = td->overroot;
s = td;
}
}
if (ue->op == TOKdotti)
{
DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ue;
TemplateInstance *ti = new TemplateInstance(loc, s->ident);
ti->tiargs = dti->ti->tiargs; // for better diagnostic message
if (!ti->updateTempDecl(sc, s))
return new ErrorExp();
return new ScopeExp(loc, ti);
}
else
{
//printf("-searchUFCS() %s\n", s->toChars());
return new DsymbolExp(loc, s);
}
}
/******************************
* 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;
}
/******************************
* Pull out callable entity with UFCS.
*/
Expression *resolveUFCS(Scope *sc, CallExp *ce)
{
Loc loc = ce->loc;
Expression *eleft;
Expression *e;
if (ce->e1->op == TOKdotid)
{
DotIdExp *die = (DotIdExp *)ce->e1;
Identifier *ident = die->ident;
Expression *ex = semanticX(die, sc);
if (ex != die)
{
ce->e1 = ex;
return NULL;
}
eleft = die->e1;
Type *t = eleft->type->toBasetype();
if (t->ty == Tarray || t->ty == Tsarray ||
t->ty == Tnull || (t->isTypeBasic() && t->ty != Tvoid))
{
/* Built-in types and arrays have no callable properties, so do shortcut.
* It is necessary in: e.init()
*/
}
else if (t->ty == Taarray)
{
if (ident == Id::remove)
{
/* Transform:
* aa.remove(arg) into delete aa[arg]
*/
if (!ce->arguments || ce->arguments->dim != 1)
{
ce->error("expected key as argument to aa.remove()");
return new ErrorExp();
}
if (!eleft->type->isMutable())
{
ce->error("cannot remove key from %s associative array %s",
MODtoChars(t->mod), eleft->toChars());
return new ErrorExp();
}
Expression *key = (*ce->arguments)[0];
key = semantic(key, sc);
key = resolveProperties(sc, key);
TypeAArray *taa = (TypeAArray *)t;
key = key->implicitCastTo(sc, taa->index);
if (key->checkValue())
return new ErrorExp();
semanticTypeInfo(sc, taa->index);
return new RemoveExp(loc, eleft, key);
}
}
else
{
if (Expression *ey = semanticY(die, sc, 1))
{
if (ey->op == TOKerror)
return ey;
ce->e1 = ey;
if (isDotOpDispatch(ey))
{
unsigned errors = global.startGagging();
e = semantic(ce->syntaxCopy(), sc);
if (!global.endGagging(errors))
return e;
/* fall down to UFCS */
}
else
return NULL;
}
}
e = searchUFCS(sc, die, ident);
}
else if (ce->e1->op == TOKdotti)
{
DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ce->e1;
if (Expression *ey = semanticY(dti, sc, 1))
{
ce->e1 = ey;
return NULL;
}
eleft = dti->e1;
e = searchUFCS(sc, dti, dti->ti->name);
}
else
return NULL;
// Rewrite
ce->e1 = e;
if (!ce->arguments)
ce->arguments = new Expressions();
ce->arguments->shift(eleft);
return NULL;
}
/******************************
* Pull out property with UFCS.
*/
Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NULL)
{
Loc loc = e1->loc;
Expression *eleft;
Expression *e;
if (e1->op == TOKdotid)
{
DotIdExp *die = (DotIdExp *)e1;
eleft = die->e1;
e = searchUFCS(sc, die, die->ident);
}
else if (e1->op == TOKdotti)
{
DotTemplateInstanceExp *dti;
dti = (DotTemplateInstanceExp *)e1;
eleft = dti->e1;
e = searchUFCS(sc, dti, dti->ti->name);
}
else
return NULL;
if (e == NULL)
return NULL;
// Rewrite
if (e2)
{
// run semantic without gagging
e2 = semantic(e2, sc);
/* f(e1) = e2
*/
Expression *ex = e->copy();
Expressions *a1 = new Expressions();
a1->setDim(1);
(*a1)[0] = eleft;
ex = new CallExp(loc, ex, a1);
ex = trySemantic(ex, sc);
/* f(e1, e2)
*/
Expressions *a2 = new Expressions();
a2->setDim(2);
(*a2)[0] = eleft;
(*a2)[1] = e2;
e = new CallExp(loc, e, a2);
if (ex)
{ // if fallback setter exists, gag errors
e = trySemantic(e, sc);
if (!e)
{ checkPropertyCall(ex);
ex = new AssignExp(loc, ex, e2);
return semantic(ex, sc);
}
}
else
{ // strict setter prints errors if fails
e = semantic(e, sc);
}
checkPropertyCall(e);
return e;
}
else
{
/* f(e1)
*/
Expressions *arguments = new Expressions();
arguments->setDim(1);
(*arguments)[0] = eleft;
e = new CallExp(loc, e, arguments);
e = semantic(e, sc);
checkPropertyCall(e);
return semantic(e, sc);
}
}
/******************************
* Perform semantic() on an array of Expressions.
*/
bool arrayExpressionSemantic(Expressions *exps, Scope *sc, bool preserveErrors)
{
bool err = false;
if (exps)
{
for (size_t i = 0; i < exps->dim; i++)
{
Expression *e = (*exps)[i];
if (e)
{
e = semantic(e, sc);
if (e->op == TOKerror)
err = true;
if (preserveErrors || e->op != TOKerror)
(*exps)[i] = e;
}
}
}
return err;
}
/****************************************
* 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->dim; 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->dim == 0)
{
exps->remove(i);
if (i == exps->dim)
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->dim)
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->dim == 0)
return -1;
for (size_t u = starti; u < exps->dim; u++)
{
Expression *exp = (*exps)[u];
TupleDeclaration *td = isAliasThisTuple(exp);
if (td)
{
exps->remove(u);
for (size_t i = 0; i<td->objects->dim; ++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;
}
/****************************************
* The common type is determined by applying ?: to each pair.
* Output:
* exps[] properties resolved, implicitly cast to common type, rewritten in place
* *pt if pt is not NULL, set to the common type
* Returns:
* true a semantic error was detected
*/
bool arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt)
{
/* Still have a problem with:
* ubyte[][] = [ cast(ubyte[])"hello", [1]];
* which works if the array literal is initialized top down with the ubyte[][]
* type, but fails with this function doing bottom up typing.
*/
//printf("arrayExpressionToCommonType()\n");
IntegerExp integerexp(0);
CondExp condexp(Loc(), &integerexp, NULL, NULL);
Type *t0 = NULL;
Expression *e0 = NULL; // dead-store to prevent spurious warning
size_t j0 = ~0; // dead-store to prevent spurious warning
for (size_t i = 0; i < exps->dim; i++)
{
Expression *e = (*exps)[i];
if (!e)
continue;
e = resolveProperties(sc, e);
if (!e->type)
{
e->error("%s has no value", e->toChars());
t0 = Type::terror;
continue;
}
if (e->op == TOKtype)
{
e->checkValue(); // report an error "type T has no value"
t0 = Type::terror;
continue;
}
if (e->type->ty == Tvoid)
{
// void expressions do not concur to the determination of the common
// type.
continue;
}
if (checkNonAssignmentArrayOp(e))
{
t0 = Type::terror;
continue;
}
e = doCopyOrMove(sc, e);
if (t0 && !t0->equals(e->type))
{
/* This applies ?: to merge the types. It's backwards;
* ?: should call this function to merge types.
*/
condexp.type = NULL;
condexp.e1 = e0;
condexp.e2 = e;
condexp.loc = e->loc;
Expression *ex = semantic(&condexp, sc);
if (ex->op == TOKerror)
e = ex;
else
{
(*exps)[j0] = condexp.e1;
e = condexp.e2;
}
}
j0 = i;
e0 = e;
t0 = e->type;
if (e->op != TOKerror)
(*exps)[i] = e;
}
if (!t0)
t0 = Type::tvoid; // [] is typed as void[]
else if (t0->ty != Terror)
{
for (size_t i = 0; i < exps->dim; i++)
{
Expression *e = (*exps)[i];
if (!e)
continue;
e = e->implicitCastTo(sc, t0);
//assert(e->op != TOKerror);
if (e->op == TOKerror)
{
/* Bugzilla 13024: a workaround for the bug in typeMerge -
* it should paint e1 and e2 by deduced common type,
* but doesn't in this particular case.
*/
t0 = Type::terror;
break;
}
(*exps)[i] = e;
}
}
if (pt)
*pt = t0;
return (t0 == Type::terror);
}
/****************************************
* 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;
}
/********************************************
* Issue an error if default construction is disabled for type t.
* Default construction is required for arrays and 'out' parameters.
* Returns:
* true an error was issued
*/
bool checkDefCtor(Loc loc, Type *t)
{
t = t->baseElemOf();
if (t->ty == Tstruct)
{
StructDeclaration *sd = ((TypeStruct *)t)->sym;
if (sd->noDefaultCtor)
{
sd->error(loc, "default construction is disabled");
return true;
}
}
return false;
}
/*********************************************
* 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;
tmp->semantic(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;
}
/****************************************
* Now that we know the exact type of the function we're calling,
* the arguments[] need to be adjusted:
* 1. implicitly convert argument to the corresponding parameter type
* 2. add default arguments for any missing arguments
* 3. do default promotions on arguments corresponding to ...
* 4. add hidden _arguments[] argument
* 5. call copy constructor for struct value arguments
* Input:
* tf type of the function
* fd the function being called, NULL if called indirectly
* Output:
* *prettype return type of function
* *peprefix expression to execute before arguments[] are evaluated, NULL if none
* Returns:
* true errors happened
*/
bool functionParameters(Loc loc, Scope *sc, TypeFunction *tf,
Type *tthis, Expressions *arguments, FuncDeclaration *fd, Type **prettype, Expression **peprefix)
{
//printf("functionParameters()\n");
assert(arguments);
assert(fd || tf->next);
size_t nargs = arguments ? arguments->dim : 0;
size_t nparams = Parameter::dim(tf->parameters);
unsigned olderrors = global.errors;
bool err = false;
*prettype = Type::terror;
Expression *eprefix = NULL;
*peprefix = NULL;
if (nargs > nparams && tf->varargs == 0)
{
error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", (ulonglong)nparams, (ulonglong)nargs, tf->toChars());
return true;
}
// If inferring return type, and semantic3() needs to be run if not already run
if (!tf->next && fd->inferRetType)
{
fd->functionSemantic();
}
else if (fd && fd->parent)
{
TemplateInstance *ti = fd->parent->isTemplateInstance();
if (ti && ti->tempdecl)
{
fd->functionSemantic3();
}
}
bool isCtorCall = fd && fd->needThis() && fd->isCtorDeclaration();
size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
/* If the function return type has wildcards in it, we'll need to figure out the actual type
* based on the actual argument types.
*/
MOD wildmatch = 0;
if (tthis && tf->isWild() && !isCtorCall)
{
Type *t = tthis;
if (t->isImmutable())
wildmatch = MODimmutable;
else if (t->isWildConst())
wildmatch = MODwildconst;
else if (t->isWild())
wildmatch = MODwild;
else if (t->isConst())
wildmatch = MODconst;
else
wildmatch = MODmutable;
}
int done = 0;
for (size_t i = 0; i < n; i++)
{
Expression *arg;
if (i < nargs)
arg = (*arguments)[i];
else
arg = NULL;
if (i < nparams)
{
Parameter *p = Parameter::getNth(tf->parameters, i);
if (!arg)
{
if (!p->defaultArg)
{
if (tf->varargs == 2 && i + 1 == nparams)
goto L2;
error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs);
return true;
}
arg = p->defaultArg;
arg = inlineCopy(arg, sc);
// __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__
arg = arg->resolveLoc(loc, sc);
arguments->push(arg);
nargs++;
}
if (tf->varargs == 2 && i + 1 == nparams)
{
//printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars());
{
MATCH m;
if ((m = arg->implicitConvTo(p->type)) > MATCHnomatch)
{
if (p->type->nextOf() && arg->implicitConvTo(p->type->nextOf()) >= m)
goto L2;
else if (nargs != nparams)
{ error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs);
return true;
}
goto L1;
}
}
L2:
Type *tb = p->type->toBasetype();
Type *tret = p->isLazyArray();
switch (tb->ty)
{
case Tsarray:
case Tarray:
{
/* Create a static array variable v of type arg->type:
* T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ];
*
* The array literal in the initializer of the hidden variable
* is now optimized. See Bugzilla 2356.
*/
Type *tbn = ((TypeArray *)tb)->next;
Expressions *elements = new Expressions();
elements->setDim(nargs - i);
for (size_t u = 0; u < elements->dim; u++)
{
Expression *a = (*arguments)[i + u];
if (tret && a->implicitConvTo(tret))
{
a = a->implicitCastTo(sc, tret);
a = a->optimize(WANTvalue);
a = toDelegate(a, a->type, sc);
}
else
a = a->implicitCastTo(sc, tbn);
(*elements)[u] = a;
}
// Bugzilla 14395: Convert to a static array literal, or its slice.
arg = new ArrayLiteralExp(loc, tbn->sarrayOf(nargs - i), elements);
if (tb->ty == Tarray)
{
arg = new SliceExp(loc, arg, NULL, NULL);
arg->type = p->type;
}
break;
}
case Tclass:
{
/* Set arg to be:
* new Tclass(arg0, arg1, ..., argn)
*/
Expressions *args = new Expressions();
args->setDim(nargs - i);
for (size_t u = i; u < nargs; u++)
(*args)[u - i] = (*arguments)[u];
arg = new NewExp(loc, NULL, NULL, p->type, args);
break;
}
default:
if (!arg)
{
error(loc, "not enough arguments");
return true;
}
break;
}
arg = semantic(arg, sc);
//printf("\targ = '%s'\n", arg->toChars());
arguments->setDim(i + 1);
(*arguments)[i] = arg;
nargs = i + 1;
done = 1;
}
L1:
if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
{
bool isRef = (p->storageClass & (STCref | STCout)) != 0;
if (unsigned char wm = arg->type->deduceWild(p->type, isRef))
{
if (wildmatch)
wildmatch = MODmerge(wildmatch, wm);
else
wildmatch = wm;
//printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p->type->toChars(), arg->type->toChars(), wm, wildmatch);
}
}
}
if (done)
break;
}
if ((wildmatch == MODmutable || wildmatch == MODimmutable) &&
tf->next->hasWild() &&
(tf->isref || !tf->next->implicitConvTo(tf->next->immutableOf())))
{
if (fd)
{
/* If the called function may return the reference to
* outer inout data, it should be rejected.
*
* void foo(ref inout(int) x) {
* ref inout(int) bar(inout(int)) { return x; }
* struct S { ref inout(int) bar() inout { return x; } }
* bar(int.init) = 1; // bad!
* S().bar() = 1; // bad!
* }
*/
Dsymbol *s = NULL;
if (fd->isThis() || fd->isNested())
s = fd->toParent2();
for (; s; s = s->toParent2())
{
if (AggregateDeclaration *ad = s->isAggregateDeclaration())
{
if (ad->isNested())
continue;
break;
}
if (FuncDeclaration *ff = s->isFuncDeclaration())
{
if (((TypeFunction *)ff->type)->iswild)
goto Linouterr;
if (ff->isNested() || ff->isThis())
continue;
}
break;
}
}
else if (tf->isWild())
{
Linouterr:
const char *s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch);
error(loc, "modify inout to %s is not allowed inside inout function", s);
return true;
}
}
assert(nargs >= nparams);
for (size_t i = 0; i < nargs; i++)
{
Expression *arg = (*arguments)[i];
assert(arg);
if (i < nparams)
{
Parameter *p = Parameter::getNth(tf->parameters, i);
if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
{
Type *tprm = p->type;
if (p->type->hasWild())
tprm = p->type->substWildTo(wildmatch);
if (!tprm->equals(arg->type))
{
//printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars());
arg = arg->implicitCastTo(sc, tprm);
arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0);
}
}
if (p->storageClass & STCref)
{
arg = arg->toLvalue(sc, arg);
// Look for mutable misaligned pointer, etc., in @safe mode
err |= checkUnsafeAccess(sc, arg, false, true);
}
else if (p->storageClass & STCout)
{
Type *t = arg->type;
if (!t->isMutable() || !t->isAssignable()) // check blit assignable
{
arg->error("cannot modify struct %s with immutable members", arg->toChars());
err = true;
}
else
{
// Look for misaligned pointer, etc., in @safe mode
err |= checkUnsafeAccess(sc, arg, false, true);
err |= checkDefCtor(arg->loc, t); // t must be default constructible
}
arg = arg->toLvalue(sc, arg);
}
else if (p->storageClass & STClazy)
{
// Convert lazy argument to a delegate
if (p->type->ty == Tvoid)
arg = toDelegate(arg, p->type, sc);
else
arg = toDelegate(arg, arg->type, sc);
}
//printf("arg: %s\n", arg->toChars());
//printf("type: %s\n", arg->type->toChars());
if (tf->parameterEscapes(p))
{
/* Argument value can escape from the called function.
* Check arg to see if it matters.
*/
if (global.params.vsafe)
err |= checkParamArgumentEscape(sc, fd, p->ident, arg, false);
}
else
{
/* Argument value cannot escape from the called function.
*/
Expression *a = arg;
if (a->op == TOKcast)
a = ((CastExp *)a)->e1;
if (a->op == TOKfunction)
{
/* Function literals can only appear once, so if this
* appearance was scoped, there cannot be any others.
*/
FuncExp *fe = (FuncExp *)a;
fe->fd->tookAddressOf = 0;
}
else if (a->op == TOKdelegate)
{
/* For passing a delegate to a scoped parameter,
* this doesn't count as taking the address of it.
* We only worry about 'escaping' references to the function.
*/
DelegateExp *de = (DelegateExp *)a;
if (de->e1->op == TOKvar)
{ VarExp *ve = (VarExp *)de->e1;
FuncDeclaration *f = ve->var->isFuncDeclaration();
if (f)
{ f->tookAddressOf--;
//printf("tookAddressOf = %d\n", f->tookAddressOf);
}
}
}
}
arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0);
}
else
{
// These will be the trailing ... arguments
// If not D linkage, do promotions
if (tf->linkage != LINKd)
{
// Promote bytes, words, etc., to ints
arg = integralPromotions(arg, sc);
// Promote floats to doubles
switch (arg->type->ty)
{
case Tfloat32:
arg = arg->castTo(sc, Type::tfloat64);
break;
case Timaginary32:
arg = arg->castTo(sc, Type::timaginary64);
break;
}
if (tf->varargs == 1)
{
const char *p = tf->linkage == LINKc ? "extern(C)" : "extern(C++)";
if (arg->type->ty == Tarray)
{
arg->error("cannot pass dynamic arrays to %s vararg functions", p);
err = true;
}
if (arg->type->ty == Tsarray)
{
arg->error("cannot pass static arrays to %s vararg functions", p);
err = true;
}
}
}
// Do not allow types that need destructors
if (arg->type->needsDestruction())
{
arg->error("cannot pass types that need destruction as variadic arguments");
err = true;
}
// Convert static arrays to dynamic arrays
// BUG: I don't think this is right for D2
Type *tb = arg->type->toBasetype();
if (tb->ty == Tsarray)
{
TypeSArray *ts = (TypeSArray *)tb;
Type *ta = ts->next->arrayOf();
if (ts->size(arg->loc) == 0)
arg = new NullExp(arg->loc, ta);
else
arg = arg->castTo(sc, ta);
}
if (tb->ty == Tstruct)
{
//arg = callCpCtor(sc, arg);
}
// Give error for overloaded function addresses
if (arg->op == TOKsymoff)
{ SymOffExp *se = (SymOffExp *)arg;
if (se->hasOverloads &&
!se->var->isFuncDeclaration()->isUnique())
{ arg->error("function %s is overloaded", arg->toChars());
err = true;
}
}
if (arg->checkValue())
err = true;
arg = arg->optimize(WANTvalue);
}
(*arguments)[i] = arg;
}
/* Remaining problems:
* 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is
* implemented by calling a function) we'll defer this for now.
* 2. value structs (or static arrays of them) that need to be copy constructed
* 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the
* function gets called (functions normally destroy their parameters)
* 2 and 3 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned
* up properly. Pushing arguments on the stack then cannot fail.
*/
if (1)
{
/* TODO: tackle problem 1)
*/
const bool leftToRight = true; // TODO: something like !fd.isArrayOp
if (!leftToRight)
assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity
const ptrdiff_t start = (leftToRight ? 0 : (ptrdiff_t)nargs - 1);
const ptrdiff_t end = (leftToRight ? (ptrdiff_t)nargs : -1);
const ptrdiff_t step = (leftToRight ? 1 : -1);
/* Compute indices of last throwing argument and first arg needing destruction.
* Used to not set up destructors unless an arg needs destruction on a throw
* in a later argument.
*/
ptrdiff_t lastthrow = -1;
ptrdiff_t firstdtor = -1;
for (ptrdiff_t i = start; i != end; i += step)
{
Expression *arg = (*arguments)[i];
if (canThrow(arg, sc->func, false))
lastthrow = i;
if (firstdtor == -1 && arg->type->needsDestruction())
{
Parameter *p = (i >= (ptrdiff_t)nparams ? NULL : Parameter::getNth(tf->parameters, i));
if (!(p && (p->storageClass & (STClazy | STCref | STCout))))
firstdtor = i;
}
}
/* Does problem 3) apply to this call?
*/
const bool needsPrefix = (firstdtor >= 0 && lastthrow >= 0
&& (lastthrow - firstdtor) * step > 0);
/* If so, initialize 'eprefix' by declaring the gate
*/
VarDeclaration *gate = NULL;
if (needsPrefix)
{
// eprefix => bool __gate [= false]
Identifier *idtmp = Identifier::generateId("__gate");
gate = new VarDeclaration(loc, Type::tbool, idtmp, NULL);
gate->storage_class |= STCtemp | STCctfe | STCvolatile;
gate->semantic(sc);
Expression *ae = new DeclarationExp(loc, gate);
eprefix = semantic(ae, sc);
}
for (ptrdiff_t i = start; i != end; i += step)
{
Expression *arg = (*arguments)[i];
Parameter *parameter = (i >= (ptrdiff_t)nparams ? NULL : Parameter::getNth(tf->parameters, i));
const bool isRef = (parameter && (parameter->storageClass & (STCref | STCout)));
const bool isLazy = (parameter && (parameter->storageClass & STClazy));
/* Skip lazy parameters
*/
if (isLazy)
continue;
/* Do we have a gate? Then we have a prefix and we're not yet past the last throwing arg.
* Declare a temporary variable for this arg and append that declaration to 'eprefix',
* which will implicitly take care of potential problem 2) for this arg.
* 'eprefix' will therefore finally contain all args up to and including the last
* potentially throwing arg, excluding all lazy parameters.
*/
if (gate)
{
const bool needsDtor = (!isRef && arg->type->needsDestruction() && i != lastthrow);
/* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor)
*/
VarDeclaration *tmp = copyToTemp(0,
needsDtor ? "__pfx" : "__pfy",
!isRef ? arg : arg->addressOf());
tmp->semantic(sc);
/* Modify the destructor so it only runs if gate==false, i.e.,
* only if there was a throw while constructing the args
*/
if (!needsDtor)
{
if (tmp->edtor)
{
assert(i == lastthrow);
tmp->edtor = NULL;
}
}
else
{
// edtor => (__gate || edtor)
assert(tmp->edtor);
Expression *e = tmp->edtor;
e = new OrOrExp(e->loc, new VarExp(e->loc, gate), e);
tmp->edtor = semantic(e, sc);
//printf("edtor: %s\n", tmp->edtor->toChars());
}
// eprefix => (eprefix, auto __pfx/y = arg)
DeclarationExp *ae = new DeclarationExp(loc, tmp);
eprefix = Expression::combine(eprefix, semantic(ae, sc));
// arg => __pfx/y
arg = new VarExp(loc, tmp);
arg = semantic(arg, sc);
if (isRef)
{
arg = new PtrExp(loc, arg);
arg = semantic(arg, sc);
}
/* Last throwing arg? Then finalize eprefix => (eprefix, gate = true),
* i.e., disable the dtors right after constructing the last throwing arg.
* From now on, the callee will take care of destructing the args because
* the args are implicitly moved into function parameters.
*
* Set gate to null to let the next iterations know they don't need to
* append to eprefix anymore.
*/
if (i == lastthrow)
{
Expression *e = new AssignExp(gate->loc, new VarExp(gate->loc, gate), new IntegerExp(gate->loc, 1, Type::tbool));
eprefix = Expression::combine(eprefix, semantic(e, sc));
gate = NULL;
}
}
else
{
/* No gate, no prefix to append to.
* Handle problem 2) by calling the copy constructor for value structs
* (or static arrays of them) if appropriate.
*/
Type *tv = arg->type->baseElemOf();
if (!isRef && tv->ty == Tstruct)
arg = doCopyOrMove(sc, arg);
}
(*arguments)[i] = arg;
}
}
//if (eprefix) printf("eprefix: %s\n", eprefix->toChars());
// If D linkage and variadic, add _arguments[] as first argument
if (tf->linkage == LINKd && tf->varargs == 1)
{
assert(arguments->dim >= nparams);
Parameters *args = new Parameters;
args->setDim(arguments->dim - nparams);
for (size_t i = 0; i < arguments->dim - nparams; i++)
{
Parameter *arg = new Parameter(STCin, (*arguments)[nparams + i]->type, NULL, NULL);
(*args)[i] = arg;
}
TypeTuple *tup = new TypeTuple(args);
Expression *e = new TypeidExp(loc, tup);
e = semantic(e, sc);
arguments->insert(0, e);
}
Type *tret = tf->next;
if (isCtorCall)
{
//printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd->toChars(), fd->type->toChars(),
// wildmatch, tf->isWild(), fd->isolateReturn());
if (!tthis)
{
assert(sc->intypeof || global.errors);
tthis = fd->isThis()->type->addMod(fd->type->mod);
}
if (tf->isWild() && !fd->isolateReturn())
{
if (wildmatch)
tret = tret->substWildTo(wildmatch);
int offset;
if (!tret->implicitConvTo(tthis) &&
!(MODimplicitConv(tret->mod, tthis->mod) && tret->isBaseOf(tthis, &offset) && offset == 0))
{
const char* s1 = tret ->isNaked() ? " mutable" : tret ->modToChars();
const char* s2 = tthis->isNaked() ? " mutable" : tthis->modToChars();
::error(loc, "inout constructor %s creates%s object, not%s",
fd->toPrettyChars(), s1, s2);
err = true;
}
}
tret = tthis;
}
else if (wildmatch && tret)
{
/* Adjust function return type based on wildmatch
*/
//printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars());
tret = tret->substWildTo(wildmatch);
}
*prettype = tret;
*peprefix = eprefix;
return (err || olderrors != global.errors);
}
/******************************** 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.extractString();
}
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;
}
/***************************************
* 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();
}
void Expression::checkDeprecated(Scope *sc, Dsymbol *s)
{
s->checkDeprecated(loc, sc);
}
/*********************************************
* 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.peekString(), ff->kind(), ff->toPrettyChars(), vbuf.peekString(), 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)
{
// Bugzilla 11395: Require TypeInfo generation for array concatenation
semanticTypeInfo(sc, t);
StructDeclaration *sd = ((TypeStruct *)t)->sym;
if (sd->postblit)
{
if (sd->postblit->storage_class & STCdisable)
{
sd->error(loc, "is not copyable because it is annotated with @disable");
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 = semantic(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;
}
/****************************************
* Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__ 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->dim);
for (size_t i = 0; i < a->dim; 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 == 4)
value = (d_uns32) value;
else if (Target::ptrsize == 8)
value = (d_uns64) 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);
// 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 (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 = semantic(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 = semantic(e, sc);
return e;
}
if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration())
{
//printf("'%s' is a function literal\n", fld->toChars());
e = new FuncExp(loc, fld);
return semantic(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 semantic(ie, sc);
}
if (Package *pkg = s->isPackage())
{
ScopeExp *ie = new ScopeExp(loc, pkg);
return semantic(ie, sc);
}
if (Module *mod = s->isModule())
{
ScopeExp *ie = new ScopeExp(loc, mod);
return semantic(ie, sc);
}
if (Nspace *ns = s->isNspace())
{
ScopeExp *ie = new ScopeExp(loc, ns);
return semantic(ie, sc);
}
if (Type *t = s->getType())
{
return semantic(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 = semantic(e, sc);
return e;
}
if (TemplateInstance *ti = s->isTemplateInstance())
{
ti->semantic(sc);
if (!ti->inst || ti->errors)
return new ErrorExp();
s = ti->toAlias();
if (!s->isTemplateInstance())
goto Lagain;
e = new ScopeExp(loc, ti);
e = semantic(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 = semantic(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->dim != ae->elements->dim)
return false;
if (elements->dim == 0 &&
!type->equals(ae->type))
{
return false;
}
for (size_t i = 0; i < elements->dim; 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(d_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->dim;
elems->append(ale->elements);
for (size_t i = d; i < elems->dim; 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->dim : 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->dim == 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->dim; ++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.offset / 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->dim == values->dim);
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->dim != ae->keys->dim)
return false;
size_t count = 0;
for (size_t i = 0; i < keys->dim; i++)
{
for (size_t j = 0; j < ae->keys->dim; j++)
{
if ((*keys)[i]->equals((*ae->keys)[j]))
{
if (!(*values)[i]->equals((*ae->values)[j]))
return false;
++count;
}
}
}
return count == keys->dim;
}
return false;
}
Expression *AssocArrayLiteralExp::syntaxCopy()
{
return new AssocArrayLiteralExp(loc,
arraySyntaxCopy(keys), arraySyntaxCopy(values));
}
bool AssocArrayLiteralExp::isBool(bool result)
{
size_t dim = keys->dim;
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->dim != se->elements->dim)
return false;
for (size_t i = 0; i < elements->dim; 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 = semantic(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.dim - 1 && sd->isNested())
return NULL;
assert(i < (int)elements->dim);
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 &&
e->type->needsNested())
{
StructLiteralExp *se = (StructLiteralExp *)e;
se->useStaticInit = true;
}
}
}
return e;
}
/************************************
* Get index of field.
* Returns -1 if not found.
*/
int StructLiteralExp::getFieldIndex(Type *type, unsigned offset)
{
/* Find which field offset is by looking at the field offsets
*/
if (elements->dim)
{
for (size_t i = 0; i < sd->fields.dim; i++)
{
VarDeclaration *v = sd->fields[i];
if (offset == v->offset &&
type->size() == v->type->size())
{
/* context field might not be filled. */
if (i == sd->fields.dim - 1 && sd->isNested())
return (int)i;
Expression *e = (*elements)[i];
if (e)
{
return (int)i;
}
break;
}
}
}
return -1;
}
/************************ TypeDotIdExp ************************************/
/* Things like:
* int.size
* foo.size
* (foo).size
* cast(foo).size
*/
DotIdExp *typeDotIdExp(Loc loc, Type *type, Identifier *ident)
{
return new DotIdExp(loc, new TypeExp(loc, type), ident);
}
/************************************************************/
// Mainly just a placeholder
TypeExp::TypeExp(Loc loc, Type *type)
: Expression(loc, TOKtype, sizeof(TypeExp))
{
//printf("TypeExp::TypeExp(%s)\n", type->toChars());
this->type = type;
}
Expression *TypeExp::syntaxCopy()
{
return new TypeExp(loc, type->syntaxCopy());
}
bool TypeExp::checkType()
{
error("type %s is not an expression", toChars());
return true;
}
bool TypeExp::checkValue()
{
error("type %s has no value", toChars());
return true;
}
/************************************************************/
/***********************************************************
* Mainly just a placeholder of
* Package, Module, Nspace, and TemplateInstance (including TemplateMixin)
*
* A template instance that requires IFTI:
* foo!tiargs(fargs) // foo!tiargs
* is left until CallExp::semantic() or resolveProperties()
*/
ScopeExp::ScopeExp(Loc loc, ScopeDsymbol *sds)
: Expression(loc, TOKscope, sizeof(ScopeExp))
{
//printf("ScopeExp::ScopeExp(sds = '%s')\n", sds->toChars());
//static int count; if (++count == 38) *(char*)0=0;
this->sds = sds;
assert(!sds->isTemplateDeclaration()); // instead, you should use TemplateExp
}
Expression *ScopeExp::syntaxCopy()
{
return new ScopeExp(loc, (ScopeDsymbol *)sds->syntaxCopy(NULL));
}
bool ScopeExp::checkType()
{
if (sds->isPackage())
{
error("%s %s has no type", sds->kind(), sds->toChars());
return true;
}
if (TemplateInstance *ti = sds->isTemplateInstance())
{
//assert(ti->needsTypeInference(sc));
if (ti->tempdecl &&
ti->semantictiargsdone &&
ti->semanticRun == PASSinit)
{
error("partial %s %s has no type", sds->kind(), toChars());
return true;
}
}
return false;
}
bool ScopeExp::checkValue()
{
error("%s %s has no value", sds->kind(), sds->toChars());
return true;
}
/********************** TemplateExp **************************************/
// Mainly just a placeholder
TemplateExp::TemplateExp(Loc loc, TemplateDeclaration *td, FuncDeclaration *fd)
: Expression(loc, TOKtemplate, sizeof(TemplateExp))
{
//printf("TemplateExp(): %s\n", td->toChars());
this->td = td;
this->fd = fd;
}
bool TemplateExp::checkType()
{
error("%s %s has no type", td->kind(), toChars());
return true;
}
bool TemplateExp::checkValue()
{
error("%s %s has no value", td->kind(), toChars());
return true;
}
bool TemplateExp::isLvalue()
{
return fd != NULL;
}
Expression *TemplateExp::toLvalue(Scope *sc, Expression *e)
{
if (!fd)
return Expression::toLvalue(sc, e);
assert(sc);
return resolve(loc, sc, fd, true);
}
/********************** NewExp **************************************/
/* thisexp.new(newargs) newtype(arguments) */
NewExp::NewExp(Loc loc, Expression *thisexp, Expressions *newargs,
Type *newtype, Expressions *arguments)
: Expression(loc, TOKnew, sizeof(NewExp))
{
this->thisexp = thisexp;
this->newargs = newargs;
this->newtype = newtype;
this->arguments = arguments;
argprefix = NULL;
member = NULL;
allocator = NULL;
onstack = 0;
}
NewExp *NewExp::create(Loc loc, Expression *thisexp, Expressions *newargs,
Type *newtype, Expressions *arguments)
{
return new NewExp(loc, thisexp, newargs, newtype, arguments);
}
Expression *NewExp::syntaxCopy()
{
return new NewExp(loc,
thisexp ? thisexp->syntaxCopy() : NULL,
arraySyntaxCopy(newargs),
newtype->syntaxCopy(), arraySyntaxCopy(arguments));
}
/********************** NewAnonClassExp **************************************/
NewAnonClassExp::NewAnonClassExp(Loc loc, Expression *thisexp,
Expressions *newargs, ClassDeclaration *cd, Expressions *arguments)
: Expression(loc, TOKnewanonclass, sizeof(NewAnonClassExp))
{
this->thisexp = thisexp;
this->newargs = newargs;
this->cd = cd;
this->arguments = arguments;
}
Expression *NewAnonClassExp::syntaxCopy()
{
return new NewAnonClassExp(loc,
thisexp ? thisexp->syntaxCopy() : NULL,
arraySyntaxCopy(newargs),
(ClassDeclaration *)cd->syntaxCopy(NULL),
arraySyntaxCopy(arguments));
}
/********************** SymbolExp **************************************/
SymbolExp::SymbolExp(Loc loc, TOK op, int size, Declaration *var, bool hasOverloads)
: Expression(loc, op, size)
{
assert(var);
this->var = var;
this->hasOverloads = hasOverloads;
}
/********************** SymOffExp **************************************/
SymOffExp::SymOffExp(Loc loc, Declaration *var, dinteger_t offset, bool hasOverloads)
: SymbolExp(loc, TOKsymoff, sizeof(SymOffExp), var,
var->isVarDeclaration() ? false : hasOverloads)
{
if (VarDeclaration *v = var->isVarDeclaration())
{
// FIXME: This error report will never be handled anyone.
// It should be done before the SymOffExp construction.
if (v->needThis())
::error(loc, "need 'this' for address of %s", v->toChars());
}
this->offset = offset;
}
bool SymOffExp::isBool(bool result)
{
return result ? true : false;
}
/******************************** VarExp **************************/
VarExp::VarExp(Loc loc, Declaration *var, bool hasOverloads)
: SymbolExp(loc, TOKvar, sizeof(VarExp), var,
var->isVarDeclaration() ? false : hasOverloads)
{
//printf("VarExp(this = %p, '%s', loc = %s)\n", this, var->toChars(), loc.toChars());
//if (strcmp(var->ident->toChars(), "func") == 0) halt();
this->type = var->type;
}
VarExp *VarExp::create(Loc loc, Declaration *var, bool hasOverloads)
{
return new VarExp(loc, var, hasOverloads);
}
bool VarExp::equals(RootObject *o)
{
if (this == o)
return true;
if (((Expression *)o)->op == TOKvar)
{
VarExp *ne = (VarExp *)o;
if (type->toHeadMutable()->equals(ne->type->toHeadMutable()) &&
var == ne->var)
{
return true;
}
}
return false;
}
bool VarExp::isLvalue()
{
if (var->storage_class & (STClazy | STCrvalue | STCmanifest))
return false;
return true;
}
Expression *VarExp::toLvalue(Scope *, Expression *)
{
if (var->storage_class & STCmanifest)
{
error("manifest constant '%s' is not lvalue", var->toChars());
return new ErrorExp();
}
if (var->storage_class & STClazy)
{
error("lazy variables cannot be lvalues");
return new ErrorExp();
}
if (var->ident == Id::ctfe)
{
error("compiler-generated variable __ctfe is not an lvalue");
return new ErrorExp();
}
if (var->ident == Id::dollar) // Bugzilla 13574
{
error("'$' is not an lvalue");
return new ErrorExp();
}
return this;
}
int VarExp::checkModifiable(Scope *sc, int flag)
{
//printf("VarExp::checkModifiable %s", toChars());
assert(type);
return var->checkModify(loc, sc, type, NULL, flag);
}
Expression *VarExp::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("VarExp::modifiableLvalue('%s')\n", var->toChars());
if (var->storage_class & STCmanifest)
{
error("cannot modify manifest constant '%s'", toChars());
return new ErrorExp();
}
// See if this expression is a modifiable lvalue (i.e. not const)
return Expression::modifiableLvalue(sc, e);
}
/******************************** OverExp **************************/
OverExp::OverExp(Loc loc, OverloadSet *s)
: Expression(loc, TOKoverloadset, sizeof(OverExp))
{
//printf("OverExp(this = %p, '%s')\n", this, var->toChars());
vars = s;
type = Type::tvoid;
}
bool OverExp::isLvalue()
{
return true;
}
Expression *OverExp::toLvalue(Scope *, Expression *)
{
return this;
}
/******************************** TupleExp **************************/
TupleExp::TupleExp(Loc loc, Expression *e0, Expressions *exps)
: Expression(loc, TOKtuple, sizeof(TupleExp))
{
//printf("TupleExp(this = %p)\n", this);
this->e0 = e0;
this->exps = exps;
}
TupleExp::TupleExp(Loc loc, Expressions *exps)
: Expression(loc, TOKtuple, sizeof(TupleExp))
{
//printf("TupleExp(this = %p)\n", this);
this->e0 = NULL;
this->exps = exps;
}
TupleExp::TupleExp(Loc loc, TupleDeclaration *tup)
: Expression(loc, TOKtuple, sizeof(TupleExp))
{
this->e0 = NULL;
this->exps = new Expressions();
this->exps->reserve(tup->objects->dim);
for (size_t i = 0; i < tup->objects->dim; i++)
{ RootObject *o = (*tup->objects)[i];
if (Dsymbol *s = getDsymbol(o))
{
/* If tuple element represents a symbol, translate to DsymbolExp
* to supply implicit 'this' if needed later.
*/
Expression *e = new DsymbolExp(loc, s);
this->exps->push(e);
}
else if (o->dyncast() == DYNCAST_EXPRESSION)
{
Expression *e = ((Expression *)o)->copy();
e->loc = loc; // Bugzilla 15669
this->exps->push(e);
}
else if (o->dyncast() == DYNCAST_TYPE)
{
Type *t = (Type *)o;
Expression *e = new TypeExp(loc, t);
this->exps->push(e);
}
else
{
error("%s is not an expression", o->toChars());
}
}
}
bool TupleExp::equals(RootObject *o)
{
if (this == o)
return true;
if (((Expression *)o)->op == TOKtuple)
{
TupleExp *te = (TupleExp *)o;
if (exps->dim != te->exps->dim)
return false;
if ((e0 && !e0->equals(te->e0)) || (!e0 && te->e0))
return false;
for (size_t i = 0; i < exps->dim; i++)
{
Expression *e1 = (*exps)[i];
Expression *e2 = (*te->exps)[i];
if (!e1->equals(e2))
return false;
}
return true;
}
return false;
}
Expression *TupleExp::syntaxCopy()
{
return new TupleExp(loc, e0 ? e0->syntaxCopy() : NULL, arraySyntaxCopy(exps));
}
/******************************** FuncExp *********************************/
FuncExp::FuncExp(Loc loc, Dsymbol *s)
: Expression(loc, TOKfunction, sizeof(FuncExp))
{
this->td = s->isTemplateDeclaration();
this->fd = s->isFuncLiteralDeclaration();
if (td)
{
assert(td->literal);
assert(td->members && td->members->dim == 1);
fd = (*td->members)[0]->isFuncLiteralDeclaration();
}
tok = fd->tok; // save original kind of function/delegate/(infer)
assert(fd->fbody);
}
bool FuncExp::equals(RootObject *o)
{
if (this == o)
return true;
if (o->dyncast() != DYNCAST_EXPRESSION)
return false;
if (((Expression *)o)->op == TOKfunction)
{
FuncExp *fe = (FuncExp *)o;
return fd == fe->fd;
}
return false;
}
void FuncExp::genIdent(Scope *sc)
{
if (fd->ident == Id::empty)
{
const char *s;
if (fd->fes) s = "__foreachbody";
else if (fd->tok == TOKreserved) s = "__lambda";
else if (fd->tok == TOKdelegate) s = "__dgliteral";
else s = "__funcliteral";
DsymbolTable *symtab;
if (FuncDeclaration *func = sc->parent->isFuncDeclaration())
{
if (func->localsymtab == NULL)
{
// Inside template constraint, symtab is not set yet.
// Initialize it lazily.
func->localsymtab = new DsymbolTable();
}
symtab = func->localsymtab;
}
else
{
ScopeDsymbol *sds = sc->parent->isScopeDsymbol();
if (!sds->symtab)
{
// Inside template constraint, symtab may not be set yet.
// Initialize it lazily.
assert(sds->isTemplateInstance());
sds->symtab = new DsymbolTable();
}
symtab = sds->symtab;
}
assert(symtab);
int num = (int)dmd_aaLen(symtab->tab) + 1;
Identifier *id = Identifier::generateId(s, num);
fd->ident = id;
if (td) td->ident = id;
symtab->insert(td ? (Dsymbol *)td : (Dsymbol *)fd);
}
}
Expression *FuncExp::syntaxCopy()
{
if (td)
return new FuncExp(loc, td->syntaxCopy(NULL));
else if (fd->semanticRun == PASSinit)
return new FuncExp(loc, fd->syntaxCopy(NULL));
else // Bugzilla 13481: Prevent multiple semantic analysis of lambda body.
return new FuncExp(loc, fd);
}
MATCH FuncExp::matchType(Type *to, Scope *sc, FuncExp **presult, int flag)
{
//printf("FuncExp::matchType('%s'), to=%s\n", type ? type->toChars() : "null", to->toChars());
if (presult)
*presult = NULL;
TypeFunction *tof = NULL;
if (to->ty == Tdelegate)
{
if (tok == TOKfunction)
{
if (!flag)
error("cannot match function literal to delegate type '%s'", to->toChars());
return MATCHnomatch;
}
tof = (TypeFunction *)to->nextOf();
}
else if (to->ty == Tpointer && to->nextOf()->ty == Tfunction)
{
if (tok == TOKdelegate)
{
if (!flag)
error("cannot match delegate literal to function pointer type '%s'", to->toChars());
return MATCHnomatch;
}
tof = (TypeFunction *)to->nextOf();
}
if (td)
{
if (!tof)
{
L1:
if (!flag)
error("cannot infer parameter types from %s", to->toChars());
return MATCHnomatch;
}
// Parameter types inference from 'tof'
assert(td->_scope);
TypeFunction *tf = (TypeFunction *)fd->type;
//printf("\ttof = %s\n", tof->toChars());
//printf("\ttf = %s\n", tf->toChars());
size_t dim = Parameter::dim(tf->parameters);
if (Parameter::dim(tof->parameters) != dim ||
tof->varargs != tf->varargs)
goto L1;
Objects *tiargs = new Objects();
tiargs->reserve(td->parameters->dim);
for (size_t i = 0; i < td->parameters->dim; i++)
{
TemplateParameter *tp = (*td->parameters)[i];
size_t u = 0;
for (; u < dim; u++)
{
Parameter *p = Parameter::getNth(tf->parameters, u);
if (p->type->ty == Tident &&
((TypeIdentifier *)p->type)->ident == tp->ident)
{
break;
}
}
assert(u < dim);
Parameter *pto = Parameter::getNth(tof->parameters, u);
Type *t = pto->type;
if (t->ty == Terror)
goto L1;
tiargs->push(t);
}
// Set target of return type inference
if (!tf->next && tof->next)
fd->treq = to;
TemplateInstance *ti = new TemplateInstance(loc, td, tiargs);
Expression *ex = new ScopeExp(loc, ti);
ex = ::semantic(ex, td->_scope);
// Reset inference target for the later re-semantic
fd->treq = NULL;
if (ex->op == TOKerror)
return MATCHnomatch;
if (ex->op != TOKfunction)
goto L1;
return ((FuncExp *)ex)->matchType(to, sc, presult, flag);
}
if (!tof || !tof->next)
return MATCHnomatch;
assert(type && type != Type::tvoid);
TypeFunction *tfx = (TypeFunction *)fd->type;
bool convertMatch = (type->ty != to->ty);
if (fd->inferRetType && tfx->next->implicitConvTo(tof->next) == MATCHconvert)
{
/* If return type is inferred and covariant return,
* tweak return statements to required return type.
*
* interface I {}
* class C : Object, I{}
*
* I delegate() dg = delegate() { return new class C(); }
*/
convertMatch = true;
TypeFunction *tfy = new TypeFunction(tfx->parameters, tof->next, tfx->varargs, tfx->linkage, STCundefined);
tfy->mod = tfx->mod;
tfy->isnothrow = tfx->isnothrow;
tfy->isnogc = tfx->isnogc;
tfy->purity = tfx->purity;
tfy->isproperty = tfx->isproperty;
tfy->isref = tfx->isref;
tfy->iswild = tfx->iswild;
tfy->deco = tfy->merge()->deco;
tfx = tfy;
}
Type *tx;
if (tok == TOKdelegate ||
(tok == TOKreserved && (type->ty == Tdelegate ||
(type->ty == Tpointer && to->ty == Tdelegate))))
{
// Allow conversion from implicit function pointer to delegate
tx = new TypeDelegate(tfx);
tx->deco = tx->merge()->deco;
}
else
{
assert(tok == TOKfunction ||
(tok == TOKreserved && type->ty == Tpointer));
tx = tfx->pointerTo();
}
//printf("\ttx = %s, to = %s\n", tx->toChars(), to->toChars());
MATCH m = tx->implicitConvTo(to);
if (m > MATCHnomatch)
{
// MATCHexact: exact type match
// MATCHconst: covairiant type match (eg. attributes difference)
// MATCHconvert: context conversion
m = convertMatch ? MATCHconvert : tx->equals(to) ? MATCHexact : MATCHconst;
if (presult)
{
(*presult) = (FuncExp *)copy();
(*presult)->type = to;
// Bugzilla 12508: Tweak function body for covariant returns.
(*presult)->fd->modifyReturns(sc, tof->next);
}
}
else if (!flag)
{
error("cannot implicitly convert expression (%s) of type %s to %s",
toChars(), tx->toChars(), to->toChars());
}
return m;
}
const char *FuncExp::toChars()
{
return fd->toChars();
}
bool FuncExp::checkType()
{
if (td)
{
error("template lambda has no type");
return true;
}
return false;
}
bool FuncExp::checkValue()
{
if (td)
{
error("template lambda has no value");
return true;
}
return false;
}
/******************************** DeclarationExp **************************/
DeclarationExp::DeclarationExp(Loc loc, Dsymbol *declaration)
: Expression(loc, TOKdeclaration, sizeof(DeclarationExp))
{
this->declaration = declaration;
}
Expression *DeclarationExp::syntaxCopy()
{
return new DeclarationExp(loc, declaration->syntaxCopy(NULL));
}
bool DeclarationExp::hasCode()
{
if (VarDeclaration *vd = declaration->isVarDeclaration())
{
return !(vd->storage_class & (STCmanifest | STCstatic));
}
return false;
}
/************************ TypeidExp ************************************/
/*
* typeid(int)
*/
TypeidExp::TypeidExp(Loc loc, RootObject *o)
: Expression(loc, TOKtypeid, sizeof(TypeidExp))
{
this->obj = o;
}
Expression *TypeidExp::syntaxCopy()
{
return new TypeidExp(loc, objectSyntaxCopy(obj));
}
/************************ TraitsExp ************************************/
/*
* __traits(identifier, args...)
*/
TraitsExp::TraitsExp(Loc loc, Identifier *ident, Objects *args)
: Expression(loc, TOKtraits, sizeof(TraitsExp))
{
this->ident = ident;
this->args = args;
}
Expression *TraitsExp::syntaxCopy()
{
return new TraitsExp(loc, ident, TemplateInstance::arraySyntaxCopy(args));
}
/************************************************************/
HaltExp::HaltExp(Loc loc)
: Expression(loc, TOKhalt, sizeof(HaltExp))
{
}
/************************************************************/
IsExp::IsExp(Loc loc, Type *targ, Identifier *id, TOK tok,
Type *tspec, TOK tok2, TemplateParameters *parameters)
: Expression(loc, TOKis, sizeof(IsExp))
{
this->targ = targ;
this->id = id;
this->tok = tok;
this->tspec = tspec;
this->tok2 = tok2;
this->parameters = parameters;
}
Expression *IsExp::syntaxCopy()
{
// This section is identical to that in TemplateDeclaration::syntaxCopy()
TemplateParameters *p = NULL;
if (parameters)
{
p = new TemplateParameters();
p->setDim(parameters->dim);
for (size_t i = 0; i < p->dim; i++)
(*p)[i] = (*parameters)[i]->syntaxCopy();
}
return new IsExp(loc,
targ->syntaxCopy(),
id,
tok,
tspec ? tspec->syntaxCopy() : NULL,
tok2,
p);
}
void unSpeculative(Scope *sc, RootObject *o);
/************************************************************/
UnaExp::UnaExp(Loc loc, TOK op, int size, Expression *e1)
: Expression(loc, op, size)
{
this->e1 = e1;
this->att1 = NULL;
}
Expression *UnaExp::syntaxCopy()
{
UnaExp *e = (UnaExp *)copy();
e->type = NULL;
e->e1 = e->e1->syntaxCopy();
return e;
}
/********************************
* The type for a unary expression is incompatible.
* Print error message.
* Returns:
* ErrorExp
*/
Expression *UnaExp::incompatibleTypes()
{
if (e1->type->toBasetype() == Type::terror)
return e1;
if (e1->op == TOKtype)
{
error("incompatible type for (%s(%s)): cannot use '%s' with types",
Token::toChars(op), e1->toChars(), Token::toChars(op));
}
else
{
error("incompatible type for (%s(%s)): '%s'",
Token::toChars(op), e1->toChars(), e1->type->toChars());
}
return new ErrorExp();
}
Expression *UnaExp::resolveLoc(Loc loc, Scope *sc)
{
e1 = e1->resolveLoc(loc, sc);
return this;
}
/************************************************************/
BinExp::BinExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2)
: Expression(loc, op, size)
{
this->e1 = e1;
this->e2 = e2;
this->att1 = NULL;
this->att2 = NULL;
}
Expression *BinExp::syntaxCopy()
{
BinExp *e = (BinExp *)copy();
e->type = NULL;
e->e1 = e->e1->syntaxCopy();
e->e2 = e->e2->syntaxCopy();
return e;
}
Expression *BinExp::checkOpAssignTypes(Scope *sc)
{
// At that point t1 and t2 are the merged types. type is the original type of the lhs.
Type *t1 = e1->type;
Type *t2 = e2->type;
// T opAssign floating yields a floating. Prevent truncating conversions (float to int).
// See issue 3841.
// Should we also prevent double to float (type->isfloating() && type->size() < t2 ->size()) ?
if (op == TOKaddass || op == TOKminass ||
op == TOKmulass || op == TOKdivass || op == TOKmodass ||
op == TOKpowass)
{
if ((type->isintegral() && t2->isfloating()))
{
warning("%s %s %s is performing truncating conversion",
type->toChars(), Token::toChars(op), t2->toChars());
}
}
// generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
if (op == TOKmulass || op == TOKdivass || op == TOKmodass)
{
// Any multiplication by an imaginary or complex number yields a complex result.
// r *= c, i*=c, r*=i, i*=i are all forbidden operations.
const char *opstr = Token::toChars(op);
if (t1->isreal() && t2->iscomplex())
{
error("%s %s %s is undefined. Did you mean %s %s %s.re ?",
t1->toChars(), opstr, t2->toChars(),
t1->toChars(), opstr, t2->toChars());
return new ErrorExp();
}
else if (t1->isimaginary() && t2->iscomplex())
{
error("%s %s %s is undefined. Did you mean %s %s %s.im ?",
t1->toChars(), opstr, t2->toChars(),
t1->toChars(), opstr, t2->toChars());
return new ErrorExp();
}
else if ((t1->isreal() || t1->isimaginary()) &&
t2->isimaginary())
{
error("%s %s %s is an undefined operation", t1->toChars(), opstr, t2->toChars());
return new ErrorExp();
}
}
// generate an error if this is a nonsensical += or -=, eg real += imaginary
if (op == TOKaddass || op == TOKminass)
{
// Addition or subtraction of a real and an imaginary is a complex result.
// Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
if ((t1->isreal() && (t2->isimaginary() || t2->iscomplex())) ||
(t1->isimaginary() && (t2->isreal() || t2->iscomplex())))
{
error("%s %s %s is undefined (result is complex)",
t1->toChars(), Token::toChars(op), t2->toChars());
return new ErrorExp();
}
if (type->isreal() || type->isimaginary())
{
assert(global.errors || t2->isfloating());
e2 = e2->castTo(sc, t1);
}
}
if (op == TOKmulass)
{
if (t2->isfloating())
{
if (t1->isreal())
{
if (t2->isimaginary() || t2->iscomplex())
{
e2 = e2->castTo(sc, t1);
}
}
else if (t1->isimaginary())
{
if (t2->isimaginary() || t2->iscomplex())
{
switch (t1->ty)
{
case Timaginary32: t2 = Type::tfloat32; break;
case Timaginary64: t2 = Type::tfloat64; break;
case Timaginary80: t2 = Type::tfloat80; break;
default:
assert(0);
}
e2 = e2->castTo(sc, t2);
}
}
}
}
else if (op == TOKdivass)
{
if (t2->isimaginary())
{
if (t1->isreal())
{
// x/iv = i(-x/v)
// Therefore, the result is 0
e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat::zero, t1));
e2->type = t1;
Expression *e = new AssignExp(loc, e1, e2);
e->type = t1;
return e;
}
else if (t1->isimaginary())
{
Type *t3;
switch (t1->ty)
{
case Timaginary32: t3 = Type::tfloat32; break;
case Timaginary64: t3 = Type::tfloat64; break;
case Timaginary80: t3 = Type::tfloat80; break;
default:
assert(0);
}
e2 = e2->castTo(sc, t3);
Expression *e = new AssignExp(loc, e1, e2);
e->type = t1;
return e;
}
}
}
else if (op == TOKmodass)
{
if (t2->iscomplex())
{
error("cannot perform modulo complex arithmetic");
return new ErrorExp();
}
}
return this;
}
/********************************
* The types for a binary expression are incompatible.
* Print error message.
* Returns:
* ErrorExp
*/
Expression *BinExp::incompatibleTypes()
{
if (e1->type->toBasetype() == Type::terror)
return e1;
if (e2->type->toBasetype() == Type::terror)
return e2;
// CondExp uses 'a ? b : c' but we're comparing 'b : c'
TOK thisOp = (op == TOKquestion) ? TOKcolon : op;
if (e1->op == TOKtype || e2->op == TOKtype)
{
error("incompatible types for ((%s) %s (%s)): cannot use '%s' with types",
e1->toChars(), Token::toChars(thisOp), e2->toChars(), Token::toChars(op));
}
else
{
error("incompatible types for ((%s) %s (%s)): '%s' and '%s'",
e1->toChars(), Token::toChars(thisOp), e2->toChars(),
e1->type->toChars(), e2->type->toChars());
}
return new ErrorExp();
}
bool BinExp::checkIntegralBin()
{
bool r1 = e1->checkIntegral();
bool r2 = e2->checkIntegral();
return (r1 || r2);
}
bool BinExp::checkArithmeticBin()
{
bool r1 = e1->checkArithmetic();
bool r2 = e2->checkArithmetic();
return (r1 || r2);
}
/********************** BinAssignExp **************************************/
BinAssignExp::BinAssignExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2)
: BinExp(loc, op, size, e1, e2)
{
}
bool BinAssignExp::isLvalue()
{
return true;
}
Expression *BinAssignExp::toLvalue(Scope *, Expression *)
{
// Lvalue-ness will be handled in glue layer.
return this;
}
Expression *BinAssignExp::modifiableLvalue(Scope *sc, Expression *)
{
// should check e1->checkModifiable() ?
return toLvalue(sc, this);
}
/************************************************************/
CompileExp::CompileExp(Loc loc, Expression *e)
: UnaExp(loc, TOKmixin, sizeof(CompileExp), e)
{
}
/************************************************************/
ImportExp::ImportExp(Loc loc, Expression *e)
: UnaExp(loc, TOKimport, sizeof(ImportExp), e)
{
}
/************************************************************/
AssertExp::AssertExp(Loc loc, Expression *e, Expression *msg)
: UnaExp(loc, TOKassert, sizeof(AssertExp), e)
{
this->msg = msg;
}
Expression *AssertExp::syntaxCopy()
{
return new AssertExp(loc, e1->syntaxCopy(), msg ? msg->syntaxCopy() : NULL);
}
/************************************************************/
DotIdExp::DotIdExp(Loc loc, Expression *e, Identifier *ident)
: UnaExp(loc, TOKdotid, sizeof(DotIdExp), e)
{
this->ident = ident;
this->wantsym = false;
this->noderef = false;
}
DotIdExp *DotIdExp::create(Loc loc, Expression *e, Identifier *ident)
{
return new DotIdExp(loc, e, ident);
}
/********************** DotTemplateExp ***********************************/
// Mainly just a placeholder
DotTemplateExp::DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td)
: UnaExp(loc, TOKdottd, sizeof(DotTemplateExp), e)
{
this->td = td;
}
/************************************************************/
DotVarExp::DotVarExp(Loc loc, Expression *e, Declaration *var, bool hasOverloads)
: UnaExp(loc, TOKdotvar, sizeof(DotVarExp), e)
{
//printf("DotVarExp()\n");
this->var = var;
this->hasOverloads = var->isVarDeclaration() ? false : hasOverloads;
}
bool DotVarExp::isLvalue()
{
return true;
}
Expression *DotVarExp::toLvalue(Scope *, Expression *)
{
//printf("DotVarExp::toLvalue(%s)\n", toChars());
return this;
}
/***********************************************
* Mark variable v as modified if it is inside a constructor that var
* is a field in.
*/
int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1)
{
//printf("modifyFieldVar(var = %s)\n", var->toChars());
Dsymbol *s = sc->func;
while (1)
{
FuncDeclaration *fd = NULL;
if (s)
fd = s->isFuncDeclaration();
if (fd &&
((fd->isCtorDeclaration() && var->isField()) ||
(fd->isStaticCtorDeclaration() && !var->isField())) &&
fd->toParent2() == var->toParent2() &&
(!e1 || e1->op == TOKthis)
)
{
bool result = true;
var->ctorinit = true;
//printf("setting ctorinit\n");
if (var->isField() && sc->fieldinit && !sc->intypeof)
{
assert(e1);
bool mustInit = ((var->storage_class & STCnodefaultctor) != 0 ||
var->type->needsNested());
size_t dim = sc->fieldinit_dim;
AggregateDeclaration *ad = fd->isMember2();
assert(ad);
size_t i;
for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ?
{
if (ad->fields[i] == var)
break;
}
assert(i < dim);
unsigned fi = sc->fieldinit[i];
if (fi & CSXthis_ctor)
{
if (var->type->isMutable() && e1->type->isMutable())
result = false;
else
{
const char *modStr = !var->type->isMutable() ? MODtoChars(var->type->mod) : MODtoChars(e1->type->mod);
::error(loc, "%s field '%s' initialized multiple times", modStr, var->toChars());
}
}
else if (sc->noctor || (fi & CSXlabel))
{
if (!mustInit && var->type->isMutable() && e1->type->isMutable())
result = false;
else
{
const char *modStr = !var->type->isMutable() ? MODtoChars(var->type->mod) : MODtoChars(e1->type->mod);
::error(loc, "%s field '%s' initialization is not allowed in loops or after labels", modStr, var->toChars());
}
}
sc->fieldinit[i] |= CSXthis_ctor;
if (var->overlapped) // Bugzilla 15258
{
for (size_t j = 0; j < ad->fields.dim; j++)
{
VarDeclaration *v = ad->fields[j];
if (v == var || !var->isOverlappedWith(v))
continue;
v->ctorinit = true;
sc->fieldinit[j] = CSXthis_ctor;
}
}
}
else if (fd != sc->func)
{
if (var->type->isMutable())
result = false;
else if (sc->func->fes)
{
const char *p = var->isField() ? "field" : var->kind();
::error(loc, "%s %s '%s' initialization is not allowed in foreach loop",
MODtoChars(var->type->mod), p, var->toChars());
}
else
{
const char *p = var->isField() ? "field" : var->kind();
::error(loc, "%s %s '%s' initialization is not allowed in nested function '%s'",
MODtoChars(var->type->mod), p, var->toChars(), sc->func->toChars());
}
}
return result;
}
else
{
if (s)
{
s = s->toParent2();
continue;
}
}
break;
}
return false;
}
int DotVarExp::checkModifiable(Scope *sc, int flag)
{
//printf("DotVarExp::checkModifiable %s %s\n", toChars(), type->toChars());
if (checkUnsafeAccess(sc, this, false, !flag))
return 2;
if (e1->op == TOKthis)
return var->checkModify(loc, sc, type, e1, flag);
//printf("\te1 = %s\n", e1->toChars());
return e1->checkModifiable(sc, flag);
}
Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e)
{
return Expression::modifiableLvalue(sc, e);
}
/************************************************************/
/* Things like:
* foo.bar!(args)
*/
DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, Identifier *name, Objects *tiargs)
: UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e)
{
//printf("DotTemplateInstanceExp()\n");
this->ti = new TemplateInstance(loc, name);
this->ti->tiargs = tiargs;
}
DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, TemplateInstance *ti)
: UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e)
{
this->ti = ti;
}
Expression *DotTemplateInstanceExp::syntaxCopy()
{
return new DotTemplateInstanceExp(loc,
e1->syntaxCopy(),
ti->name,
TemplateInstance::arraySyntaxCopy(ti->tiargs));
}
bool DotTemplateInstanceExp::findTempDecl(Scope *sc)
{
if (ti->tempdecl)
return true;
Expression *e = new DotIdExp(loc, e1, ti->name);
e = semantic(e, sc);
if (e->op == TOKdot)
e = ((DotExp *)e)->e2;
Dsymbol *s = NULL;
switch (e->op)
{
case TOKoverloadset: s = ((OverExp *)e)->vars; break;
case TOKdottd: s = ((DotTemplateExp *)e)->td; break;
case TOKscope: s = ((ScopeExp *)e)->sds; break;
case TOKdotvar: s = ((DotVarExp *)e)->var; break;
case TOKvar: s = ((VarExp *)e)->var; break;
default: return false;
}
return ti->updateTempDecl(sc, s);
}
/************************************************************/
DelegateExp::DelegateExp(Loc loc, Expression *e, FuncDeclaration *f, bool hasOverloads)
: UnaExp(loc, TOKdelegate, sizeof(DelegateExp), e)
{
this->func = f;
this->hasOverloads = hasOverloads;
}
/************************************************************/
DotTypeExp::DotTypeExp(Loc loc, Expression *e, Dsymbol *s)
: UnaExp(loc, TOKdottype, sizeof(DotTypeExp), e)
{
this->sym = s;
this->type = NULL;
}
/************************************************************/
CallExp::CallExp(Loc loc, Expression *e, Expressions *exps)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
this->arguments = exps;
this->f = NULL;
this->directcall = false;
}
CallExp::CallExp(Loc loc, Expression *e)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
this->arguments = NULL;
this->f = NULL;
this->directcall = false;
}
CallExp::CallExp(Loc loc, Expression *e, Expression *earg1)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
Expressions *arguments = new Expressions();
if (earg1)
{
arguments->setDim(1);
(*arguments)[0] = earg1;
}
this->arguments = arguments;
this->f = NULL;
this->directcall = false;
}
CallExp::CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
Expressions *arguments = new Expressions();
arguments->setDim(2);
(*arguments)[0] = earg1;
(*arguments)[1] = earg2;
this->arguments = arguments;
this->f = NULL;
this->directcall = false;
}
CallExp *CallExp::create(Loc loc, Expression *e, Expressions *exps)
{
return new CallExp(loc, e, exps);
}
CallExp *CallExp::create(Loc loc, Expression *e)
{
return new CallExp(loc, e);
}
CallExp *CallExp::create(Loc loc, Expression *e, Expression *earg1)
{
return new CallExp(loc, e, earg1);
}
Expression *CallExp::syntaxCopy()
{
return new CallExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
}
bool CallExp::isLvalue()
{
Type *tb = e1->type->toBasetype();
if (tb->ty == Tdelegate || tb->ty == Tpointer)
tb = tb->nextOf();
if (tb->ty == Tfunction && ((TypeFunction *)tb)->isref)
{
if (e1->op == TOKdotvar)
if (((DotVarExp *)e1)->var->isCtorDeclaration())
return false;
return true; // function returns a reference
}
return false;
}
Expression *CallExp::toLvalue(Scope *sc, Expression *e)
{
if (isLvalue())
return this;
return Expression::toLvalue(sc, e);
}
Expression *CallExp::addDtorHook(Scope *sc)
{
/* Only need to add dtor hook if it's a type that needs destruction.
* Use same logic as VarDeclaration::callScopeDtor()
*/
if (e1->type && e1->type->ty == Tfunction)
{
TypeFunction *tf = (TypeFunction *)e1->type;
if (tf->isref)
return this;
}
Type *tv = type->baseElemOf();
if (tv->ty == Tstruct)
{
TypeStruct *ts = (TypeStruct *)tv;
StructDeclaration *sd = ts->sym;
if (sd->dtor)
{
/* Type needs destruction, so declare a tmp
* which the back end will recognize and call dtor on
*/
VarDeclaration *tmp = copyToTemp(0, "__tmpfordtor", this);
DeclarationExp *de = new DeclarationExp(loc, tmp);
VarExp *ve = new VarExp(loc, tmp);
Expression *e = new CommaExp(loc, de, ve);
e = semantic(e, sc);
return e;
}
}
return this;
}
FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL)
{
if (e->op == TOKaddress)
{
Expression *ae1 = ((AddrExp *)e)->e1;
if (ae1->op == TOKvar)
{
VarExp *ve = (VarExp *)ae1;
if (hasOverloads)
*hasOverloads = ve->hasOverloads;
return ve->var->isFuncDeclaration();
}
if (ae1->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)ae1;
if (hasOverloads)
*hasOverloads = dve->hasOverloads;
return dve->var->isFuncDeclaration();
}
}
else
{
if (e->op == TOKsymoff)
{
SymOffExp *soe = (SymOffExp *)e;
if (hasOverloads)
*hasOverloads = soe->hasOverloads;
return soe->var->isFuncDeclaration();
}
if (e->op == TOKdelegate)
{
DelegateExp *dge = (DelegateExp *)e;
if (hasOverloads)
*hasOverloads = dge->hasOverloads;
return dge->func->isFuncDeclaration();
}
}
return NULL;
}
/************************************************************/
AddrExp::AddrExp(Loc loc, Expression *e)
: UnaExp(loc, TOKaddress, sizeof(AddrExp), e)
{
}
AddrExp::AddrExp(Loc loc, Expression *e, Type *t)
: UnaExp(loc, TOKaddress, sizeof(AddrExp), e)
{
type = t;
}
/************************************************************/
PtrExp::PtrExp(Loc loc, Expression *e)
: UnaExp(loc, TOKstar, sizeof(PtrExp), e)
{
// if (e->type)
// type = ((TypePointer *)e->type)->next;
}
PtrExp::PtrExp(Loc loc, Expression *e, Type *t)
: UnaExp(loc, TOKstar, sizeof(PtrExp), e)
{
type = t;
}
bool PtrExp::isLvalue()
{
return true;
}
Expression *PtrExp::toLvalue(Scope *, Expression *)
{
return this;
}
int PtrExp::checkModifiable(Scope *sc, int flag)
{
if (e1->op == TOKsymoff)
{ SymOffExp *se = (SymOffExp *)e1;
return se->var->checkModify(loc, sc, type, NULL, flag);
}
else if (e1->op == TOKaddress)
{
AddrExp *ae = (AddrExp *)e1;
return ae->e1->checkModifiable(sc, flag);
}
return 1;
}
Expression *PtrExp::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type->toChars());
return Expression::modifiableLvalue(sc, e);
}
/************************************************************/
NegExp::NegExp(Loc loc, Expression *e)
: UnaExp(loc, TOKneg, sizeof(NegExp), e)
{
}
/************************************************************/
UAddExp::UAddExp(Loc loc, Expression *e)
: UnaExp(loc, TOKuadd, sizeof(UAddExp), e)
{
}
/************************************************************/
ComExp::ComExp(Loc loc, Expression *e)
: UnaExp(loc, TOKtilde, sizeof(ComExp), e)
{
}
/************************************************************/
NotExp::NotExp(Loc loc, Expression *e)
: UnaExp(loc, TOKnot, sizeof(NotExp), e)
{
}
/************************************************************/
DeleteExp::DeleteExp(Loc loc, Expression *e, bool isRAII)
: UnaExp(loc, TOKdelete, sizeof(DeleteExp), e)
{
this->isRAII = isRAII;
}
Expression *DeleteExp::toBoolean(Scope *)
{
error("delete does not give a boolean result");
return new ErrorExp();
}
/************************************************************/
CastExp::CastExp(Loc loc, Expression *e, Type *t)
: UnaExp(loc, TOKcast, sizeof(CastExp), e)
{
this->to = t;
this->mod = (unsigned char)~0;
}
/* For cast(const) and cast(immutable)
*/
CastExp::CastExp(Loc loc, Expression *e, unsigned char mod)
: UnaExp(loc, TOKcast, sizeof(CastExp), e)
{
this->to = NULL;
this->mod = mod;
}
Expression *CastExp::syntaxCopy()
{
return to ? new CastExp(loc, e1->syntaxCopy(), to->syntaxCopy())
: new CastExp(loc, e1->syntaxCopy(), mod);
}
/************************************************************/
VectorExp::VectorExp(Loc loc, Expression *e, Type *t)
: UnaExp(loc, TOKvector, sizeof(VectorExp), e)
{
assert(t->ty == Tvector);
to = (TypeVector *)t;
dim = ~0;
ownedByCtfe = OWNEDcode;
}
VectorExp *VectorExp::create(Loc loc, Expression *e, Type *t)
{
return new VectorExp(loc, e, t);
}
Expression *VectorExp::syntaxCopy()
{
return new VectorExp(loc, e1->syntaxCopy(), to->syntaxCopy());
}
/************************************************************/
VectorArrayExp::VectorArrayExp(Loc loc, Expression *e1)
: UnaExp(loc, TOKvectorarray, sizeof(VectorExp), e1)
{
}
bool VectorArrayExp::isLvalue()
{
return e1->isLvalue();
}
Expression *VectorArrayExp::toLvalue(Scope *sc, Expression *e)
{
e1 = e1->toLvalue(sc, e);
return this;
}
/************************************************************/
SliceExp::SliceExp(Loc loc, Expression *e1, IntervalExp *ie)
: UnaExp(loc, TOKslice, sizeof(SliceExp), e1)
{
this->upr = ie ? ie->upr : NULL;
this->lwr = ie ? ie->lwr : NULL;
lengthVar = NULL;
upperIsInBounds = false;
lowerIsLessThanUpper = false;
arrayop = false;
}
SliceExp::SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr)
: UnaExp(loc, TOKslice, sizeof(SliceExp), e1)
{
this->upr = upr;
this->lwr = lwr;
lengthVar = NULL;
upperIsInBounds = false;
lowerIsLessThanUpper = false;
arrayop = false;
}
Expression *SliceExp::syntaxCopy()
{
SliceExp *se = new SliceExp(loc, e1->syntaxCopy(),
lwr ? lwr->syntaxCopy() : NULL,
upr ? upr->syntaxCopy() : NULL);
se->lengthVar = this->lengthVar; // bug7871
return se;
}
int SliceExp::checkModifiable(Scope *sc, int flag)
{
//printf("SliceExp::checkModifiable %s\n", toChars());
if (e1->type->ty == Tsarray ||
(e1->op == TOKindex && e1->type->ty != Tarray) ||
e1->op == TOKslice)
{
return e1->checkModifiable(sc, flag);
}
return 1;
}
bool SliceExp::isLvalue()
{
/* slice expression is rvalue in default, but
* conversion to reference of static array is only allowed.
*/
return (type && type->toBasetype()->ty == Tsarray);
}
Expression *SliceExp::toLvalue(Scope *sc, Expression *e)
{
//printf("SliceExp::toLvalue(%s) type = %s\n", toChars(), type ? type->toChars() : NULL);
return (type && type->toBasetype()->ty == Tsarray)
? this : Expression::toLvalue(sc, e);
}
Expression *SliceExp::modifiableLvalue(Scope *, Expression *)
{
error("slice expression %s is not a modifiable lvalue", toChars());
return this;
}
bool SliceExp::isBool(bool result)
{
return e1->isBool(result);
}
/********************** ArrayLength **************************************/
ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1)
: UnaExp(loc, TOKarraylength, sizeof(ArrayLengthExp), e1)
{
}
Expression *opAssignToOp(Loc loc, TOK op, Expression *e1, Expression *e2)
{ Expression *e;
switch (op)
{
case TOKaddass: e = new AddExp(loc, e1, e2); break;
case TOKminass: e = new MinExp(loc, e1, e2); break;
case TOKmulass: e = new MulExp(loc, e1, e2); break;
case TOKdivass: e = new DivExp(loc, e1, e2); break;
case TOKmodass: e = new ModExp(loc, e1, e2); break;
case TOKandass: e = new AndExp(loc, e1, e2); break;
case TOKorass: e = new OrExp (loc, e1, e2); break;
case TOKxorass: e = new XorExp(loc, e1, e2); break;
case TOKshlass: e = new ShlExp(loc, e1, e2); break;
case TOKshrass: e = new ShrExp(loc, e1, e2); break;
case TOKushrass: e = new UshrExp(loc, e1, e2); break;
default: assert(0);
}
return e;
}
/*********************
* Rewrite:
* array.length op= e2
* as:
* array.length = array.length op e2
* or:
* auto tmp = &array;
* (*tmp).length = (*tmp).length op e2
*/
Expression *ArrayLengthExp::rewriteOpAssign(BinExp *exp)
{
Expression *e;
assert(exp->e1->op == TOKarraylength);
ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1;
if (ale->e1->op == TOKvar)
{
e = opAssignToOp(exp->loc, exp->op, ale, exp->e2);
e = new AssignExp(exp->loc, ale->syntaxCopy(), e);
}
else
{
/* auto tmp = &array;
* (*tmp).length = (*tmp).length op e2
*/
VarDeclaration *tmp = copyToTemp(0, "__arraylength", new AddrExp(ale->loc, ale->e1));
Expression *e1 = new ArrayLengthExp(ale->loc, new PtrExp(ale->loc, new VarExp(ale->loc, tmp)));
Expression *elvalue = e1->syntaxCopy();
e = opAssignToOp(exp->loc, exp->op, e1, exp->e2);
e = new AssignExp(exp->loc, elvalue, e);
e = new CommaExp(exp->loc, new DeclarationExp(ale->loc, tmp), e);
}
return e;
}
/*********************** IntervalExp ********************************/
// Mainly just a placeholder
IntervalExp::IntervalExp(Loc loc, Expression *lwr, Expression *upr)
: Expression(loc, TOKinterval, sizeof(IntervalExp))
{
this->lwr = lwr;
this->upr = upr;
}
Expression *IntervalExp::syntaxCopy()
{
return new IntervalExp(loc, lwr->syntaxCopy(), upr->syntaxCopy());
}
/********************** DelegatePtrExp **************************************/
DelegatePtrExp::DelegatePtrExp(Loc loc, Expression *e1)
: UnaExp(loc, TOKdelegateptr, sizeof(DelegatePtrExp), e1)
{
}
bool DelegatePtrExp::isLvalue()
{
return e1->isLvalue();
}
Expression *DelegatePtrExp::toLvalue(Scope *sc, Expression *e)
{
e1 = e1->toLvalue(sc, e);
return this;
}
Expression *DelegatePtrExp::modifiableLvalue(Scope *sc, Expression *e)
{
if (sc->func->setUnsafe())
{
error("cannot modify delegate pointer in @safe code %s", toChars());
return new ErrorExp();
}
return Expression::modifiableLvalue(sc, e);
}
/********************** DelegateFuncptrExp **************************************/
DelegateFuncptrExp::DelegateFuncptrExp(Loc loc, Expression *e1)
: UnaExp(loc, TOKdelegatefuncptr, sizeof(DelegateFuncptrExp), e1)
{
}
bool DelegateFuncptrExp::isLvalue()
{
return e1->isLvalue();
}
Expression *DelegateFuncptrExp::toLvalue(Scope *sc, Expression *e)
{
e1 = e1->toLvalue(sc, e);
return this;
}
Expression *DelegateFuncptrExp::modifiableLvalue(Scope *sc, Expression *e)
{
if (sc->func->setUnsafe())
{
error("cannot modify delegate function pointer in @safe code %s", toChars());
return new ErrorExp();
}
return Expression::modifiableLvalue(sc, e);
}
/*********************** ArrayExp *************************************/
// e1 [ i1, i2, i3, ... ]
ArrayExp::ArrayExp(Loc loc, Expression *e1, Expression *index)
: UnaExp(loc, TOKarray, sizeof(ArrayExp), e1)
{
arguments = new Expressions();
if (index)
arguments->push(index);
lengthVar = NULL;
currentDimension = 0;
}
ArrayExp::ArrayExp(Loc loc, Expression *e1, Expressions *args)
: UnaExp(loc, TOKarray, sizeof(ArrayExp), e1)
{
arguments = args;
lengthVar = NULL;
currentDimension = 0;
}
Expression *ArrayExp::syntaxCopy()
{
ArrayExp *ae = new ArrayExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
ae->lengthVar = this->lengthVar; // bug7871
return ae;
}
bool ArrayExp::isLvalue()
{
if (type && type->toBasetype()->ty == Tvoid)
return false;
return true;
}
Expression *ArrayExp::toLvalue(Scope *, Expression *)
{
if (type && type->toBasetype()->ty == Tvoid)
error("voids have no value");
return this;
}
/************************* DotExp ***********************************/
DotExp::DotExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKdot, sizeof(DotExp), e1, e2)
{
}
/************************* CommaExp ***********************************/
CommaExp::CommaExp(Loc loc, Expression *e1, Expression *e2, bool generated)
: BinExp(loc, TOKcomma, sizeof(CommaExp), e1, e2)
{
isGenerated = generated;
allowCommaExp = generated;
}
bool CommaExp::isLvalue()
{
return e2->isLvalue();
}
Expression *CommaExp::toLvalue(Scope *sc, Expression *)
{
e2 = e2->toLvalue(sc, NULL);
return this;
}
int CommaExp::checkModifiable(Scope *sc, int flag)
{
return e2->checkModifiable(sc, flag);
}
Expression *CommaExp::modifiableLvalue(Scope *sc, Expression *e)
{
e2 = e2->modifiableLvalue(sc, e);
return this;
}
bool CommaExp::isBool(bool result)
{
return e2->isBool(result);
}
Expression *CommaExp::toBoolean(Scope *sc)
{
Expression *ex2 = e2->toBoolean(sc);
if (ex2->op == TOKerror)
return ex2;
e2 = ex2;
type = e2->type;
return this;
}
Expression *CommaExp::addDtorHook(Scope *sc)
{
e2 = e2->addDtorHook(sc);
return this;
}
/************************** IndexExp **********************************/
// e1 [ e2 ]
IndexExp::IndexExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKindex, sizeof(IndexExp), e1, e2)
{
//printf("IndexExp::IndexExp('%s')\n", toChars());
lengthVar = NULL;
modifiable = false; // assume it is an rvalue
indexIsInBounds = false;
}
Expression *IndexExp::syntaxCopy()
{
IndexExp *ie = new IndexExp(loc, e1->syntaxCopy(), e2->syntaxCopy());
ie->lengthVar = this->lengthVar; // bug7871
return ie;
}
bool IndexExp::isLvalue()
{
return true;
}
Expression *IndexExp::toLvalue(Scope *, Expression *)
{
return this;
}
int IndexExp::checkModifiable(Scope *sc, int flag)
{
if (e1->type->ty == Tsarray ||
e1->type->ty == Taarray ||
(e1->op == TOKindex && e1->type->ty != Tarray) ||
e1->op == TOKslice)
{
return e1->checkModifiable(sc, flag);
}
return 1;
}
Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("IndexExp::modifiableLvalue(%s)\n", toChars());
Expression *ex = markSettingAAElem();
if (ex->op == TOKerror)
return ex;
return Expression::modifiableLvalue(sc, e);
}
Expression *IndexExp::markSettingAAElem()
{
if (e1->type->toBasetype()->ty == Taarray)
{
Type *t2b = e2->type->toBasetype();
if (t2b->ty == Tarray && t2b->nextOf()->isMutable())
{
error("associative arrays can only be assigned values with immutable keys, not %s", e2->type->toChars());
return new ErrorExp();
}
modifiable = true;
if (e1->op == TOKindex)
{
Expression *ex = ((IndexExp *)e1)->markSettingAAElem();
if (ex->op == TOKerror)
return ex;
assert(ex == e1);
}
}
return this;
}
/************************* PostExp ***********************************/
PostExp::PostExp(TOK op, Loc loc, Expression *e)
: BinExp(loc, op, sizeof(PostExp), e,
new IntegerExp(loc, 1, Type::tint32))
{
}
/************************* PreExp ***********************************/
PreExp::PreExp(TOK op, Loc loc, Expression *e)
: UnaExp(loc, op, sizeof(PreExp), e)
{
}
/************************************************************/
/* op can be TOKassign, TOKconstruct, or TOKblit */
AssignExp::AssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKassign, sizeof(AssignExp), e1, e2)
{
memset = 0;
}
bool AssignExp::isLvalue()
{
// Array-op 'x[] = y[]' should make an rvalue.
// Setting array length 'x.length = v' should make an rvalue.
if (e1->op == TOKslice ||
e1->op == TOKarraylength)
{
return false;
}
return true;
}
Expression *AssignExp::toLvalue(Scope *sc, Expression *ex)
{
if (e1->op == TOKslice ||
e1->op == TOKarraylength)
{
return Expression::toLvalue(sc, ex);
}
/* In front-end level, AssignExp should make an lvalue of e1.
* Taking the address of e1 will be handled in low level layer,
* so this function does nothing.
*/
return this;
}
Expression *AssignExp::toBoolean(Scope *)
{
// Things like:
// if (a = b) ...
// are usually mistakes.
error("assignment cannot be used as a condition, perhaps == was meant?");
return new ErrorExp();
}
/************************************************************/
ConstructExp::ConstructExp(Loc loc, Expression *e1, Expression *e2)
: AssignExp(loc, e1, e2)
{
op = TOKconstruct;
}
ConstructExp::ConstructExp(Loc loc, VarDeclaration *v, Expression *e2)
: AssignExp(loc, new VarExp(loc, v), e2)
{
assert(v->type && e1->type);
op = TOKconstruct;
if (v->storage_class & (STCref | STCout))
memset |= referenceInit;
}
/************************************************************/
BlitExp::BlitExp(Loc loc, Expression *e1, Expression *e2)
: AssignExp(loc, e1, e2)
{
op = TOKblit;
}
BlitExp::BlitExp(Loc loc, VarDeclaration *v, Expression *e2)
: AssignExp(loc, new VarExp(loc, v), e2)
{
assert(v->type && e1->type);
op = TOKblit;
if (v->storage_class & (STCref | STCout))
memset |= referenceInit;
}
/************************************************************/
AddAssignExp::AddAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKaddass, sizeof(AddAssignExp), e1, e2)
{
}
/************************************************************/
MinAssignExp::MinAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKminass, sizeof(MinAssignExp), e1, e2)
{
}
/************************************************************/
CatAssignExp::CatAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKcatass, sizeof(CatAssignExp), e1, e2)
{
}
/************************************************************/
MulAssignExp::MulAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKmulass, sizeof(MulAssignExp), e1, e2)
{
}
/************************************************************/
DivAssignExp::DivAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKdivass, sizeof(DivAssignExp), e1, e2)
{
}
/************************************************************/
ModAssignExp::ModAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKmodass, sizeof(ModAssignExp), e1, e2)
{
}
/************************************************************/
ShlAssignExp::ShlAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKshlass, sizeof(ShlAssignExp), e1, e2)
{
}
/************************************************************/
ShrAssignExp::ShrAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKshrass, sizeof(ShrAssignExp), e1, e2)
{
}
/************************************************************/
UshrAssignExp::UshrAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKushrass, sizeof(UshrAssignExp), e1, e2)
{
}
/************************************************************/
AndAssignExp::AndAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKandass, sizeof(AndAssignExp), e1, e2)
{
}
/************************************************************/
OrAssignExp::OrAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKorass, sizeof(OrAssignExp), e1, e2)
{
}
/************************************************************/
XorAssignExp::XorAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKxorass, sizeof(XorAssignExp), e1, e2)
{
}
/***************** PowAssignExp *******************************************/
PowAssignExp::PowAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinAssignExp(loc, TOKpowass, sizeof(PowAssignExp), e1, e2)
{
}
/************************* AddExp *****************************/
AddExp::AddExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKadd, sizeof(AddExp), e1, e2)
{
}
/************************************************************/
MinExp::MinExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmin, sizeof(MinExp), e1, e2)
{
}
/************************* CatExp *****************************/
CatExp::CatExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKcat, sizeof(CatExp), e1, e2)
{
}
/************************************************************/
MulExp::MulExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmul, sizeof(MulExp), e1, e2)
{
}
/************************************************************/
DivExp::DivExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKdiv, sizeof(DivExp), e1, e2)
{
}
/************************************************************/
ModExp::ModExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmod, sizeof(ModExp), e1, e2)
{
}
/************************************************************/
PowExp::PowExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKpow, sizeof(PowExp), e1, e2)
{
}
/************************************************************/
ShlExp::ShlExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKshl, sizeof(ShlExp), e1, e2)
{
}
/************************************************************/
ShrExp::ShrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKshr, sizeof(ShrExp), e1, e2)
{
}
/************************************************************/
UshrExp::UshrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKushr, sizeof(UshrExp), e1, e2)
{
}
/************************************************************/
AndExp::AndExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKand, sizeof(AndExp), e1, e2)
{
}
/************************************************************/
OrExp::OrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKor, sizeof(OrExp), e1, e2)
{
}
/************************************************************/
XorExp::XorExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKxor, sizeof(XorExp), e1, e2)
{
}
/************************************************************/
OrOrExp::OrOrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKoror, sizeof(OrOrExp), e1, e2)
{
}
Expression *OrOrExp::toBoolean(Scope *sc)
{
Expression *ex2 = e2->toBoolean(sc);
if (ex2->op == TOKerror)
return ex2;
e2 = ex2;
return this;
}
/************************************************************/
AndAndExp::AndAndExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKandand, sizeof(AndAndExp), e1, e2)
{
}
Expression *AndAndExp::toBoolean(Scope *sc)
{
Expression *ex2 = e2->toBoolean(sc);
if (ex2->op == TOKerror)
return ex2;
e2 = ex2;
return this;
}
/************************************************************/
InExp::InExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKin, sizeof(InExp), e1, e2)
{
}
/************************************************************/
/* This deletes the key e1 from the associative array e2
*/
RemoveExp::RemoveExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKremove, sizeof(RemoveExp), e1, e2)
{
type = Type::tbool;
}
/************************************************************/
CmpExp::CmpExp(TOK op, Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, op, sizeof(CmpExp), e1, e2)
{
}
/************************************************************/
EqualExp::EqualExp(TOK op, Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, op, sizeof(EqualExp), e1, e2)
{
assert(op == TOKequal || op == TOKnotequal);
}
/************************************************************/
IdentityExp::IdentityExp(TOK op, Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, op, sizeof(IdentityExp), e1, e2)
{
}
/****************************************************************/
CondExp::CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2)
: BinExp(loc, TOKquestion, sizeof(CondExp), e1, e2)
{
this->econd = econd;
}
Expression *CondExp::syntaxCopy()
{
return new CondExp(loc, econd->syntaxCopy(), e1->syntaxCopy(), e2->syntaxCopy());
}
void CondExp::hookDtors(Scope *sc)
{
class DtorVisitor : public StoppableVisitor
{
public:
Scope *sc;
CondExp *ce;
VarDeclaration *vcond;
bool isThen;
DtorVisitor(Scope *sc, CondExp *ce)
{
this->sc = sc;
this->ce = ce;
this->vcond = NULL;
}
void visit(Expression *)
{
//printf("(e = %s)\n", e->toChars());
}
void visit(DeclarationExp *e)
{
VarDeclaration *v = e->declaration->isVarDeclaration();
if (v && !v->isDataseg())
{
if (v->_init)
{
ExpInitializer *ei = v->_init->isExpInitializer();
if (ei)
ei->exp->accept(this);
}
if (v->needsScopeDtor())
{
if (!vcond)
{
vcond = copyToTemp(STCvolatile, "__cond", ce->econd);
vcond->semantic(sc);
Expression *de = new DeclarationExp(ce->econd->loc, vcond);
de = semantic(de, sc);
Expression *ve = new VarExp(ce->econd->loc, vcond);
ce->econd = Expression::combine(de, ve);
}
//printf("\t++v = %s, v->edtor = %s\n", v->toChars(), v->edtor->toChars());
Expression *ve = new VarExp(vcond->loc, vcond);
if (isThen)
v->edtor = new AndAndExp(v->edtor->loc, ve, v->edtor);
else
v->edtor = new OrOrExp(v->edtor->loc, ve, v->edtor);
v->edtor = semantic(v->edtor, sc);
//printf("\t--v = %s, v->edtor = %s\n", v->toChars(), v->edtor->toChars());
}
}
}
};
DtorVisitor v(sc, this);
//printf("+%s\n", toChars());
v.isThen = true; walkPostorder(e1, &v);
v.isThen = false; walkPostorder(e2, &v);
//printf("-%s\n", toChars());
}
bool CondExp::isLvalue()
{
return e1->isLvalue() && e2->isLvalue();
}
Expression *CondExp::toLvalue(Scope *sc, Expression *)
{
// convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
CondExp *e = (CondExp *)copy();
e->e1 = e1->toLvalue(sc, NULL)->addressOf();
e->e2 = e2->toLvalue(sc, NULL)->addressOf();
e->type = type->pointerTo();
return new PtrExp(loc, e, type);
}
int CondExp::checkModifiable(Scope *sc, int flag)
{
return e1->checkModifiable(sc, flag) && e2->checkModifiable(sc, flag);
}
Expression *CondExp::modifiableLvalue(Scope *sc, Expression *)
{
//error("conditional expression %s is not a modifiable lvalue", toChars());
e1 = e1->modifiableLvalue(sc, e1);
e2 = e2->modifiableLvalue(sc, e2);
return toLvalue(sc, this);
}
Expression *CondExp::toBoolean(Scope *sc)
{
Expression *ex1 = e1->toBoolean(sc);
Expression *ex2 = e2->toBoolean(sc);
if (ex1->op == TOKerror)
return ex1;
if (ex2->op == TOKerror)
return ex2;
e1 = ex1;
e2 = ex2;
return this;
}
/****************************************************************/
DefaultInitExp::DefaultInitExp(Loc loc, TOK subop, int size)
: Expression(loc, TOKdefault, size)
{
this->subop = subop;
}
/****************************************************************/
FileInitExp::FileInitExp(Loc loc, TOK tok)
: DefaultInitExp(loc, tok, sizeof(FileInitExp))
{
}
Expression *FileInitExp::resolveLoc(Loc loc, Scope *sc)
{
//printf("FileInitExp::resolve() %s\n", toChars());
const char *s = loc.filename ? loc.filename : sc->_module->ident->toChars();
if (subop == TOKfilefullpath)
s = FileName::combine(sc->_module->srcfilePath, s);
Expression *e = new StringExp(loc, const_cast<char *>(s));
e = semantic(e, sc);
e = e->castTo(sc, type);
return e;
}
/****************************************************************/
LineInitExp::LineInitExp(Loc loc)
: DefaultInitExp(loc, TOKline, sizeof(LineInitExp))
{
}
Expression *LineInitExp::resolveLoc(Loc loc, Scope *sc)
{
Expression *e = new IntegerExp(loc, loc.linnum, Type::tint32);
e = e->castTo(sc, type);
return e;
}
/****************************************************************/
ModuleInitExp::ModuleInitExp(Loc loc)
: DefaultInitExp(loc, TOKmodulestring, sizeof(ModuleInitExp))
{
}
Expression *ModuleInitExp::resolveLoc(Loc loc, Scope *sc)
{
const char *s;
if (sc->callsc)
s = sc->callsc->_module->toPrettyChars();
else
s = sc->_module->toPrettyChars();
Expression *e = new StringExp(loc, const_cast<char *>(s));
e = semantic(e, sc);
e = e->castTo(sc, type);
return e;
}
/****************************************************************/
FuncInitExp::FuncInitExp(Loc loc)
: DefaultInitExp(loc, TOKfuncstring, sizeof(FuncInitExp))
{
}
Expression *FuncInitExp::resolveLoc(Loc loc, Scope *sc)
{
const char *s;
if (sc->callsc && sc->callsc->func)
s = sc->callsc->func->Dsymbol::toPrettyChars();
else if (sc->func)
s = sc->func->Dsymbol::toPrettyChars();
else
s = "";
Expression *e = new StringExp(loc, const_cast<char *>(s));
e = semantic(e, sc);
e = e->castTo(sc, type);
return e;
}
/****************************************************************/
PrettyFuncInitExp::PrettyFuncInitExp(Loc loc)
: DefaultInitExp(loc, TOKprettyfunc, sizeof(PrettyFuncInitExp))
{
}
Expression *PrettyFuncInitExp::resolveLoc(Loc loc, Scope *sc)
{
FuncDeclaration *fd;
if (sc->callsc && sc->callsc->func)
fd = sc->callsc->func;
else
fd = sc->func;
const char *s;
if (fd)
{
const char *funcStr = fd->Dsymbol::toPrettyChars();
OutBuffer buf;
functionToBufferWithIdent((TypeFunction *)fd->type, &buf, funcStr);
s = buf.extractString();
}
else
{
s = "";
}
Expression *e = new StringExp(loc, const_cast<char *>(s));
e = semantic(e, sc);
e = e->castTo(sc, type);
return e;
}
/****************************************************************/
Expression *extractOpDollarSideEffect(Scope *sc, UnaExp *ue)
{
Expression *e0;
Expression *e1 = Expression::extractLast(ue->e1, &e0);
// Bugzilla 12585: Extract the side effect part if ue->e1 is comma.
if (!isTrivialExp(e1))
{
/* Even if opDollar is needed, 'e1' should be evaluate only once. So
* Rewrite:
* e1.opIndex( ... use of $ ... )
* e1.opSlice( ... use of $ ... )
* as:
* (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...)
* (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...)
*/
e1 = extractSideEffect(sc, "__dop", &e0, e1, false);
assert(e1->op == TOKvar);
VarExp *ve = (VarExp *)e1;
ve->var->storage_class |= STCexptemp; // lifetime limited to expression
}
ue->e1 = e1;
return e0;
}
/**************************************
* Runs semantic on ae->arguments. Declares temporary variables
* if '$' was used.
*/
Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0)
{
assert(!ae->lengthVar);
*pe0 = NULL;
AggregateDeclaration *ad = isAggregate(ae->e1->type);
Dsymbol *slice = search_function(ad, Id::slice);
//printf("slice = %s %s\n", slice->kind(), slice->toChars());
for (size_t i = 0; i < ae->arguments->dim; i++)
{
if (i == 0)
*pe0 = extractOpDollarSideEffect(sc, ae);
Expression *e = (*ae->arguments)[i];
if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration()))
{
Lfallback:
if (ae->arguments->dim == 1)
return NULL;
ae->error("multi-dimensional slicing requires template opSlice");
return new ErrorExp();
}
//printf("[%d] e = %s\n", i, e->toChars());
// Create scope for '$' variable for this dimension
ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae);
sym->loc = ae->loc;
sym->parent = sc->scopesym;
sc = sc->push(sym);
ae->lengthVar = NULL; // Create it only if required
ae->currentDimension = i; // Dimension for $, if required
e = semantic(e, sc);
e = resolveProperties(sc, e);
if (ae->lengthVar && sc->func)
{
// If $ was used, declare it now
Expression *de = new DeclarationExp(ae->loc, ae->lengthVar);
de = semantic(de, sc);
*pe0 = Expression::combine(*pe0, de);
}
sc = sc->pop();
if (e->op == TOKinterval)
{
IntervalExp *ie = (IntervalExp *)e;
Objects *tiargs = new Objects();
Expression *edim = new IntegerExp(ae->loc, i, Type::tsize_t);
edim = semantic(edim, sc);
tiargs->push(edim);
Expressions *fargs = new Expressions();
fargs->push(ie->lwr);
fargs->push(ie->upr);
unsigned xerrors = global.startGagging();
sc = sc->push();
FuncDeclaration *fslice = resolveFuncCall(ae->loc, sc, slice, tiargs, ae->e1->type, fargs, 1);
sc = sc->pop();
global.endGagging(xerrors);
if (!fslice)
goto Lfallback;
e = new DotTemplateInstanceExp(ae->loc, ae->e1, slice->ident, tiargs);
e = new CallExp(ae->loc, e, fargs);
e = semantic(e, sc);
}
if (!e->type)
{
ae->error("%s has no value", e->toChars());
e = new ErrorExp();
}
if (e->op == TOKerror)
return e;
(*ae->arguments)[i] = e;
}
return ae;
}
/***********************************************************
* Resolve `exp` as a compile-time known string.
* Params:
* sc = scope
* exp = Expression which expected as a string
* s = What the string is expected for, will be used in error diagnostic.
* Returns:
* String literal, or `null` if error happens.
*/
StringExp *semanticString(Scope *sc, Expression *exp, const char *s)
{
sc = sc->startCTFE();
exp = semantic(exp, sc);
exp = resolveProperties(sc, exp);
sc = sc->endCTFE();
if (exp->op == TOKerror)
return NULL;
Expression *e = exp;
if (exp->type->isString())
{
e = e->ctfeInterpret();
if (e->op == TOKerror)
return NULL;
}
StringExp *se = e->toStringExp();
if (!se)
{
exp->error("string expected for %s, not (%s) of type %s",
s, exp->toChars(), exp->type->toChars());
return NULL;
}
return se;
}
/**************************************
* Runs semantic on se->lwr and se->upr. Declares a temporary variable
* if '$' was used.
*/
Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, IntervalExp *ie, Expression **pe0)
{
//assert(!ae->lengthVar);
if (!ie)
return ae;
VarDeclaration *lengthVar = ae->lengthVar;
// create scope for '$'
ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae);
sym->loc = ae->loc;
sym->parent = sc->scopesym;
sc = sc->push(sym);
for (size_t i = 0; i < 2; ++i)
{
Expression *e = i == 0 ? ie->lwr : ie->upr;
e = semantic(e, sc);
e = resolveProperties(sc, e);
if (!e->type)
{
ae->error("%s has no value", e->toChars());
return new ErrorExp();
}
(i == 0 ? ie->lwr : ie->upr) = e;
}
if (lengthVar != ae->lengthVar && sc->func)
{
// If $ was used, declare it now
Expression *de = new DeclarationExp(ae->loc, ae->lengthVar);
de = semantic(de, sc);
*pe0 = Expression::combine(*pe0, de);
}
sc = sc->pop();
return ae;
}
Expression *BinExp::reorderSettingAAElem(Scope *sc)
{
BinExp *be = this;
if (be->e1->op != TOKindex)
return be;
IndexExp *ie = (IndexExp *)be->e1;
if (ie->e1->type->toBasetype()->ty != Taarray)
return be;
/* Fix evaluation order of setting AA element. (Bugzilla 3825)
* Rewrite:
* aa[k1][k2][k3] op= val;
* as:
* auto ref __aatmp = aa;
* auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3;
* auto ref __aaval = val;
* __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval; // assignment
*/
Expression *e0 = NULL;
while (1)
{
Expression *de = NULL;
ie->e2 = extractSideEffect(sc, "__aakey", &de, ie->e2);
e0 = Expression::combine(de, e0);
Expression *ie1 = ie->e1;
if (ie1->op != TOKindex ||
((IndexExp *)ie1)->e1->type->toBasetype()->ty != Taarray)
{
break;
}
ie = (IndexExp *)ie1;
}
assert(ie->e1->type->toBasetype()->ty == Taarray);
Expression *de = NULL;
ie->e1 = extractSideEffect(sc, "__aatmp", &de, ie->e1);
e0 = Expression::combine(de, e0);
be->e2 = extractSideEffect(sc, "__aaval", &e0, be->e2, true);
//printf("-e0 = %s, be = %s\n", e0->toChars(), be->toChars());
return Expression::combine(e0, be);
}