blob: 5ae5fe6a717dde6f76edbe3e65b9185e7ac0a070 [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
*/
#include "root/dsystem.h"
#include "root/rmem.h"
#include "root/root.h"
#include "mars.h"
#include "mangle.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 "nspace.h"
#include "ctfe.h"
#include "target.h"
bool typeMerge(Scope *sc, TOK op, Type **pt, Expression **pe1, Expression **pe2);
bool isArrayOpValid(Expression *e);
Expression *expandVar(int result, VarDeclaration *v);
bool checkAssignEscape(Scope *sc, Expression *e, bool gag);
bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag);
bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember);
bool checkNestedRef(Dsymbol *s, Dsymbol *p);
bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t istart = 0);
bool symbolIsVisible(Module *mod, Dsymbol *s);
bool symbolIsVisible(Scope *sc, Dsymbol *s);
VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e);
Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false);
Type *getTypeInfoType(Loc loc, Type *t, Scope *sc);
char *MODtoChars(MOD mod);
bool MODimplicitConv(MOD modfrom, MOD modto);
MOD MODmerge(MOD mod1, MOD mod2);
MATCH MODmethodConv(MOD modfrom, MOD modto);
void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod);
void unSpeculative(Scope *sc, RootObject *o);
bool isDotOpDispatch(Expression *e);
bool isNeedThisScope(Scope *sc, Declaration *d);
bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg);
bool isSafeCast(Expression *e, Type *tfrom, Type *tto);
FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL);
Expression *callCpCtor(Scope *sc, Expression *e);
Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads);
bool checkPrintfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list);
bool checkScanfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list);
/********************************************************
* Perform semantic analysis and CTFE on expressions to produce
* a string.
* Params:
* buf = append generated string to buffer
* sc = context
* exps = array of Expressions
* Returns:
* true on error
*/
bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps)
{
if (!exps)
return false;
for (size_t i = 0; i < exps->length; i++)
{
Expression *ex = (*exps)[i];
if (!ex)
continue;
Scope *sc2 = sc->startCTFE();
Expression *e2 = expressionSemantic(ex, sc2);
Expression *e3 = resolveProperties(sc2, e2);
sc2->endCTFE();
// allowed to contain types as well as expressions
Expression *e4 = ctfeInterpretForPragmaMsg(e3);
if (!e4 || e4->op == TOKerror)
return true;
// expand tuple
if (TupleExp *te = e4->isTupleExp())
{
if (expressionsToString(buf, sc, te->exps))
return true;
continue;
}
// char literals exp `.toStringExp` return `null` but we cant override it
// because in most contexts we don't want the conversion to succeed.
IntegerExp *ie = e4->isIntegerExp();
const TY ty = (ie && ie->type) ? ie->type->ty : (TY)Terror;
if (ty == Tchar || ty == Twchar || ty == Tdchar)
{
TypeSArray *tsa = new TypeSArray(ie->type, new IntegerExp(ex->loc, 1, Type::tint32));
e4 = new ArrayLiteralExp(ex->loc, tsa, ie);
}
if (StringExp *se = e4->toStringExp())
buf.writestring(se->toUTF8(sc)->toPtr());
else
buf.writestring(e4->toChars());
}
return false;
}
/***********************************************************
* 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 = expressionSemantic(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;
}
/****************************************************************/
static 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->length; i++)
{
if (i == 0)
*pe0 = extractOpDollarSideEffect(sc, ae);
Expression *e = (*ae->arguments)[i];
if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration()))
{
Lfallback:
if (ae->arguments->length == 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 = expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(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 = expressionSemantic(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;
}
/**************************************
* 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 = expressionSemantic(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 = expressionSemantic(de, sc);
*pe0 = Expression::combine(*pe0, de);
}
sc = sc->pop();
return ae;
}
/******************************
* 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->length; i++)
{
Expression *e = (*exps)[i];
if (e)
{
e = expressionSemantic(e, sc);
if (e->op == TOKerror)
err = true;
if (preserveErrors || e->op != TOKerror)
(*exps)[i] = e;
}
}
}
return err;
}
/******************************
* 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->semanticRun < PASSsemanticdone)
{
dsymbolSemantic(ce->f, NULL);
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;
}
// 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
*/
static 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;
// 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);
}
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);
}
}
/******************************
* Pull out callable entity with UFCS.
*/
static 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->length != 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 = expressionSemantic(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 = expressionSemantic(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.
*/
static 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 = expressionSemantic(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 expressionSemantic(ex, sc);
}
}
else
{ // strict setter prints errors if fails
e = expressionSemantic(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 = expressionSemantic(e, sc);
checkPropertyCall(e);
return expressionSemantic(e, sc);
}
}
/******************************
* 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.length; 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;
}
/*************************************************************
* 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
*/
static 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 = expressionSemantic(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 = expressionSemantic(e1, sc);
}
else
e1 = expressionSemantic(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;
}
/***************************************
* Pull out any properties.
*/
static 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 = expressionSemantic(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.length; 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 expressionSemantic(e, sc);
}
}
{
for (size_t i = 0; i < os->a.length; 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 expressionSemantic(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 = expressionSemantic(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 expressionSemantic(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 expressionSemantic(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 expressionSemantic(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 expressionSemantic(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 == 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;
}
/****************************************
* 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
*/
static 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
bool foundType = false;
for (size_t i = 0; i < exps->length; 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)
{
foundType = true; // do not break immediately, there might be more errors
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 (!foundType && 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 = expressionSemantic(&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->length; 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);
}
static 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
*/
static Expression *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;
}
/****************************************
* Preprocess arguments to function.
* Output:
* exps[] tuples expanded, properties resolved, rewritten in place
* Returns:
* true a semantic error occurred
*/
static bool preFunctionParameters(Scope *sc, Expressions *exps)
{
bool err = false;
if (exps)
{
expandTuples(exps);
for (size_t i = 0; i < exps->length; i++)
{
Expression *arg = (*exps)[i];
arg = resolveProperties(sc, arg);
if (arg->op == TOKtype)
{
arg->error("cannot pass type %s as a function argument", arg->toChars());
arg = new ErrorExp();
err = true;
}
else if (arg->type->toBasetype()->ty == Tfunction)
{
arg->error("cannot pass type %s as a function argument", arg->toChars());
arg = new ErrorExp();
err = true;
}
else if (checkNonAssignmentArrayOp(arg))
{
arg = new ErrorExp();
err = true;
}
(*exps)[i] = arg;
}
}
return err;
}
/********************************************
* 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
*/
static 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;
}
/****************************************
* 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
*/
static 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->length : 0;
size_t nparams = tf->parameterList.length();
unsigned olderrors = global.errors;
bool err = false;
*prettype = Type::terror;
Expression *eprefix = NULL;
*peprefix = NULL;
if (nargs > nparams && tf->parameterList.varargs == VARARGnone)
{
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 = tf->parameterList[i];
if (!arg)
{
if (!p->defaultArg)
{
if (tf->parameterList.varargs == VARARGtypesafe && 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->parameterList.varargs == VARARGtypesafe && 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->length; 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 = expressionSemantic(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 = tf->parameterList[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->parameterList.varargs == VARARGvariadic)
{
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;
}
/* If calling C scanf(), printf(), or any variants, check the format string against the arguments
*/
const bool isVa_list = tf->parameterList.varargs == VARARGnone;
if (fd && (fd->flags & FUNCFLAGprintf))
{
if (StringExp *se = (*arguments)[nparams - 1 - isVa_list]->isStringExp())
{
Expressions argslice;
argslice.reserve(nargs - nparams);
for (size_t i = nparams; i < nargs; i++)
argslice.push((*arguments)[i]);
checkPrintfFormat(se->loc, se->toPtr(), argslice, isVa_list);
}
}
else if (fd && (fd->flags & FUNCFLAGscanf))
{
if (StringExp *se = (*arguments)[nparams - 1 - isVa_list]->isStringExp())
{
Expressions argslice;
argslice.reserve(nargs - nparams);
for (size_t i = nparams; i < nargs; i++)
argslice.push((*arguments)[i]);
checkPrintfFormat(se->loc, se->toPtr(), argslice, isVa_list);
}
}
/* 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 : tf->parameterList[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;
dsymbolSemantic(gate, sc);
Expression *ae = new DeclarationExp(loc, gate);
eprefix = expressionSemantic(ae, sc);
}
for (ptrdiff_t i = start; i != end; i += step)
{
Expression *arg = (*arguments)[i];
Parameter *parameter = (i >= (ptrdiff_t)nparams ? NULL : tf->parameterList[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());
dsymbolSemantic(tmp, 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 LogicalExp(e->loc, TOKoror, new VarExp(e->loc, gate), e);
tmp->edtor = expressionSemantic(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, expressionSemantic(ae, sc));
// arg => __pfx/y
arg = new VarExp(loc, tmp);
arg = expressionSemantic(arg, sc);
if (isRef)
{
arg = new PtrExp(loc, arg);
arg = expressionSemantic(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, expressionSemantic(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->isDstyleVariadic())
{
assert(arguments->length >= nparams);
Parameters *args = new Parameters;
args->setDim(arguments->length - nparams);
for (size_t i = 0; i < arguments->length - nparams; i++)
{
Parameter *arg = new Parameter(STCin, (*arguments)[nparams + i]->type, NULL, NULL, NULL);
(*args)[i] = arg;
}
TypeTuple *tup = new TypeTuple(args);
Expression *e = new TypeidExp(loc, tup);
e = expressionSemantic(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);
}
/**
* Determines whether a symbol represents a module or package
* (Used as a helper for is(type == module) and is(type == package))
*
* Params:
* sym = the symbol to be checked
*
* Returns:
* the symbol which `sym` represents (or `null` if it doesn't represent a `Package`)
*/
Package *resolveIsPackage(Dsymbol *sym)
{
Package *pkg;
if (Import *imp = sym->isImport())
{
if (imp->pkg == NULL)
{
error(sym->loc, "Internal Compiler Error: unable to process forward-referenced import `%s`",
imp->toChars());
assert(0);
}
pkg = imp->pkg;
}
else if (Module *mod = sym->isModule())
pkg = mod->isPackageFile ? mod->pkg : sym->isPackage();
else
pkg = sym->isPackage();
if (pkg)
pkg->resolvePKGunknown();
return pkg;
}
static Module *loadStdMath()
{
static Import *impStdMath = NULL;
if (!impStdMath)
{
Identifiers *a = new Identifiers();
a->push(Id::std);
Import *s = new Import(Loc(), a, Id::math, NULL, false);
s->load(NULL);
if (s->mod)
{
s->mod->importAll(NULL);
dsymbolSemantic(s->mod, NULL);
}
impStdMath = s;
}
return impStdMath->mod;
}
class ExpressionSemanticVisitor : public Visitor
{
public:
Expression *result;
Scope *sc;
ExpressionSemanticVisitor(Scope *sc)
{
this->result = NULL;
this->sc = sc;
}
private:
void setError()
{
result = new ErrorExp();
}
/*********************
* Mark the operand as will never be dereferenced,
* which is useful info for @safe checks.
* Do before semantic() on operands rewrites them.
*/
static void setNoderefOperand(UnaExp *e)
{
if (e->e1->op == TOKdotid)
((DotIdExp *)e->e1)->noderef = true;
}
/*********************
* Mark the operands as will never be dereferenced,
* which is useful info for @safe checks.
* Do before semantic() on operands rewrites them.
*/
static void setNoderefOperands(BinExp *e)
{
if (e->e1->op == TOKdotid)
((DotIdExp *)e->e1)->noderef = true;
if (e->e2->op == TOKdotid)
((DotIdExp *)e->e2)->noderef = true;
}
static FuncDeclaration *resolveOverloadSet(Loc loc, Scope *sc,
OverloadSet *os, Objects* tiargs, Type *tthis, Expressions *arguments)
{
FuncDeclaration *f = NULL;
for (size_t i = 0; i < os->a.length; i++)
{
Dsymbol *s = os->a[i];
if (tiargs && s->isFuncDeclaration())
continue;
if (FuncDeclaration *f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, 1))
{
if (f2->errors)
return NULL;
if (f)
{
/* Error if match in more than one overload set,
* even if one is a 'better' match than the other.
*/
ScopeDsymbol::multiplyDefined(loc, f, f2);
}
else
f = f2;
}
}
if (!f)
::error(loc, "no overload matches for %s", os->toChars());
else if (f->errors)
f = NULL;
return f;
}
/****************************************************
* Determine if `exp`, which takes the address of `v`, can do so safely.
* Params:
* sc = context
* exp = expression that takes the address of `v`
* v = the variable getting its address taken
* Returns:
* `true` if ok, `false` for error
*/
static bool checkAddressVar(Scope *sc, UnaExp *e, VarDeclaration *v)
{
if (v)
{
if (!v->canTakeAddressOf())
{
e->error("cannot take address of %s", e->e1->toChars());
return false;
}
if (sc->func && !sc->intypeof && !v->isDataseg())
{
const char *p = v->isParameter() ? "parameter" : "local";
if (global.params.vsafe)
{
// Taking the address of v means it cannot be set to 'scope' later
v->storage_class &= ~STCmaybescope;
v->doNotInferScope = true;
if (v->storage_class & STCscope && sc->func->setUnsafe())
{
e->error("cannot take address of scope %s %s in @safe function %s", p, v->toChars(), sc->func->toChars());
return false;
}
}
else if (sc->func->setUnsafe())
{
e->error("cannot take address of %s %s in @safe function %s", p, v->toChars(), sc->func->toChars());
return false;
}
}
}
return true;
}
static bool checkVectorElem(Expression *e, Expression *elem)
{
if (elem->isConst() == 1)
return false;
e->error("constant expression expected, not %s", elem->toChars());
return true;
}
public:
void visit(Expression *e)
{
if (e->type)
e->type = typeSemantic(e->type, e->loc, sc);
else
e->type = Type::tvoid;
result = e;
}
void visit(IntegerExp *e)
{
assert(e->type);
if (e->type->ty == Terror)
return setError();
assert(e->type->deco);
e->normalize();
result = e;
}
void visit(RealExp *e)
{
if (!e->type)
e->type = Type::tfloat64;
else
e->type = typeSemantic(e->type, e->loc, sc);
result = e;
}
void visit(ComplexExp *e)
{
if (!e->type)
e->type = Type::tcomplex80;
else
e->type = typeSemantic(e->type, e->loc, sc);
result = e;
}
void visit(IdentifierExp *exp)
{
if (exp->type) // This is used as the dummy expression
{
result = exp;
return;
}
Dsymbol *scopesym;
Dsymbol *s = sc->search(exp->loc, exp->ident, &scopesym);
if (s)
{
if (s->errors)
return setError();
Expression *e;
/* See if the symbol was a member of an enclosing 'with'
*/
WithScopeSymbol *withsym = scopesym->isWithScopeSymbol();
if (withsym && withsym->withstate->wthis && symbolIsVisible(sc, s))
{
/* Disallow shadowing
*/
// First find the scope of the with
Scope *scwith = sc;
while (scwith->scopesym != scopesym)
{
scwith = scwith->enclosing;
assert(scwith);
}
// Look at enclosing scopes for symbols with the same name,
// in the same function
for (Scope *scx = scwith; scx && scx->func == scwith->func; scx = scx->enclosing)
{
Dsymbol *s2;
if (scx->scopesym && scx->scopesym->symtab &&
(s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL &&
s != s2)
{
exp->error("with symbol %s is shadowing local symbol %s", s->toPrettyChars(), s2->toPrettyChars());
return setError();
}
}
s = s->toAlias();
// Same as wthis.ident
// TODO: DotIdExp.semantic will find 'ident' from 'wthis' again.
// The redudancy should be removed.
e = new VarExp(exp->loc, withsym->withstate->wthis);
e = new DotIdExp(exp->loc, e, exp->ident);
e = expressionSemantic(e, sc);
}
else
{
if (withsym)
{
if (withsym->withstate->exp->type->ty != Tvoid)
{
// with (exp)' is a type expression
// or 's' is not visible there (for error message)
e = new TypeExp(exp->loc, withsym->withstate->exp->type);
}
else
{
// 'with (exp)' is a Package/Module
e = withsym->withstate->exp;
}
e = new DotIdExp(exp->loc, e, exp->ident);
result = expressionSemantic(e, sc);
return;
}
/* If f is really a function template,
* then replace f with the function template declaration.
*/
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
TemplateDeclaration *td = getFuncTemplateDecl(f);
if (td)
{
if (td->overroot) // if not start of overloaded list of TemplateDeclaration's
td = td->overroot; // then get the start
e = new TemplateExp(exp->loc, td, f);
e = expressionSemantic(e, sc);
result = e;
return;
}
}
// Haven't done overload resolution yet, so pass 1
e = resolve(exp->loc, sc, s, true);
}
result = e;
return;
}
if (hasThis(sc))
{
AggregateDeclaration *ad = sc->getStructClassScope();
if (ad && ad->aliasthis)
{
Expression *e;
e = new IdentifierExp(exp->loc, Id::This);
e = new DotIdExp(exp->loc, e, ad->aliasthis->ident);
e = new DotIdExp(exp->loc, e, exp->ident);
e = trySemantic(e, sc);
if (e)
{
result = e;
return;
}
}
}
if (exp->ident == Id::ctfe)
{
if (sc->flags & SCOPEctfe)
{
exp->error("variable __ctfe cannot be read at compile time");
return setError();
}
// Create the magic __ctfe bool variable
VarDeclaration *vd = new VarDeclaration(exp->loc, Type::tbool, Id::ctfe, NULL);
vd->storage_class |= STCtemp;
vd->semanticRun = PASSsemanticdone;
Expression *e = new VarExp(exp->loc, vd);
e = expressionSemantic(e, sc);
result = e;
return;
}
// If we've reached this point and are inside a with() scope then we may
// try one last attempt by checking whether the 'wthis' object supports
// dynamic dispatching via opDispatch.
// This is done by rewriting this expression as wthis.ident.
for (Scope *sc2 = sc; sc2; sc2 = sc2->enclosing)
{
if (!sc2->scopesym)
continue;
if (WithScopeSymbol *ss = sc2->scopesym->isWithScopeSymbol())
{
if (ss->withstate->wthis)
{
Expression *e;
e = new VarExp(exp->loc, ss->withstate->wthis);
e = new DotIdExp(exp->loc, e, exp->ident);
e = trySemantic(e, sc);
if (e)
{
result = e;
return;
}
}
break;
}
}
/* Look for what user might have meant
*/
if (const char *n = importHint(exp->ident->toChars()))
exp->error("`%s` is not defined, perhaps `import %s;` is needed?", exp->ident->toChars(), n);
else if (Dsymbol *s2 = sc->search_correct(exp->ident))
exp->error("undefined identifier `%s`, did you mean %s `%s`?", exp->ident->toChars(), s2->kind(), s2->toChars());
else if (const char *p = Scope::search_correct_C(exp->ident))
exp->error("undefined identifier `%s`, did you mean `%s`?", exp->ident->toChars(), p);
else
exp->error("undefined identifier `%s`", exp->ident->toChars());
return setError();
}
void visit(DsymbolExp *e)
{
result = resolve(e->loc, sc, e->s, e->hasOverloads);
}
void visit(ThisExp *e)
{
if (e->type)
{
result = e;
return;
}
FuncDeclaration *fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
/* Special case for typeof(this) and typeof(super) since both
* should work even if they are not inside a non-static member function
*/
if (!fd && sc->intypeof == 1)
{
// Find enclosing struct or class
for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent)
{
if (!s)
{
e->error("%s is not in a class or struct scope", e->toChars());
goto Lerr;
}
ClassDeclaration *cd = s->isClassDeclaration();
if (cd)
{
e->type = cd->type;
result = e;
return;
}
StructDeclaration *sd = s->isStructDeclaration();
if (sd)
{
e->type = sd->type;
result = e;
return;
}
}
}
if (!fd)
goto Lerr;
assert(fd->vthis);
e->var = fd->vthis;
assert(e->var->parent);
e->type = e->var->type;
if (e->var->checkNestedReference(sc, e->loc))
return setError();
if (!sc->intypeof)
sc->callSuper |= CSXthis;
result = e;
return;
Lerr:
e->error("`this` is only defined in non-static member functions, not %s", sc->parent->toChars());
return setError();
}
void visit(SuperExp *e)
{
if (e->type)
{
result = e;
return;
}
FuncDeclaration *fd = hasThis(sc);
ClassDeclaration *cd;
Dsymbol *s;
/* Special case for typeof(this) and typeof(super) since both
* should work even if they are not inside a non-static member function
*/
if (!fd && sc->intypeof == 1)
{
// Find enclosing class
for (s = sc->getStructClassScope(); 1; s = s->parent)
{
if (!s)
{
e->error("%s is not in a class scope", e->toChars());
goto Lerr;
}
cd = s->isClassDeclaration();
if (cd)
{
cd = cd->baseClass;
if (!cd)
{
e->error("class %s has no `super`", s->toChars());
goto Lerr;
}
e->type = cd->type;
result = e;
return;
}
}
}
if (!fd)
goto Lerr;
e->var = fd->vthis;
assert(e->var && e->var->parent);
s = fd->toParent();
while (s && s->isTemplateInstance())
s = s->toParent();
if (s->isTemplateDeclaration()) // allow inside template constraint
s = s->toParent();
assert(s);
cd = s->isClassDeclaration();
//printf("parent is %s %s\n", fd->toParent()->kind(), fd->toParent()->toChars());
if (!cd)
goto Lerr;
if (!cd->baseClass)
{
e->error("no base class for %s", cd->toChars());
e->type = e->var->type;
}
else
{
e->type = cd->baseClass->type;
e->type = e->type->castMod(e->var->type->mod);
}
if (e->var->checkNestedReference(sc, e->loc))
return setError();
if (!sc->intypeof)
sc->callSuper |= CSXsuper;
result = e;
return;
Lerr:
e->error("`super` is only allowed in non-static class member functions");
return setError();
}
void visit(NullExp *e)
{
// NULL is the same as (void *)0
if (e->type)
{
result = e;
return;
}
e->type = Type::tnull;
result = e;
}
void visit(StringExp *e)
{
if (e->type)
{
result = e;
return;
}
OutBuffer buffer;
size_t newlen = 0;
const char *p;
size_t u;
unsigned c;
switch (e->postfix)
{
case 'd':
for (u = 0; u < e->len;)
{
p = utf_decodeChar((utf8_t *)e->string, e->len, &u, &c);
if (p)
{
e->error("%s", p);
return setError();
}
else
{
buffer.write4(c);
newlen++;
}
}
buffer.write4(0);
e->string = buffer.extractData();
e->len = newlen;
e->sz = 4;
e->type = new TypeDArray(Type::tdchar->immutableOf());
e->committed = 1;
break;
case 'w':
for (u = 0; u < e->len;)
{
p = utf_decodeChar((utf8_t *)e->string, e->len, &u, &c);
if (p)
{
e->error("%s", p);
return setError();
}
else
{
buffer.writeUTF16(c);
newlen++;
if (c >= 0x10000)
newlen++;
}
}
buffer.writeUTF16(0);
e->string = buffer.extractData();
e->len = newlen;
e->sz = 2;
e->type = new TypeDArray(Type::twchar->immutableOf());
e->committed = 1;
break;
case 'c':
e->committed = 1;
/* fall through */
default:
e->type = new TypeDArray(Type::tchar->immutableOf());
break;
}
e->type = typeSemantic(e->type, e->loc, sc);
//e->type = e->type->immutableOf();
//printf("type = %s\n", e->type->toChars());
result = e;
}
void visit(ArrayLiteralExp *e)
{
if (e->type)
{
result = e;
return;
}
/* Perhaps an empty array literal [ ] should be rewritten as null?
*/
if (e->basis)
e->basis = expressionSemantic(e->basis, sc);
if (arrayExpressionSemantic(e->elements, sc) || (e->basis && e->basis->op == TOKerror))
return setError();
expandTuples(e->elements);
Type *t0;
if (e->basis)
e->elements->push(e->basis);
bool err = arrayExpressionToCommonType(sc, e->elements, &t0);
if (e->basis)
e->elements->pop();
if (err)
return setError();
e->type = t0->arrayOf();
e->type = typeSemantic(e->type, e->loc, sc);
/* Disallow array literals of type void being used.
*/
if (e->elements->length > 0 && t0->ty == Tvoid)
{
e->error("%s of type %s has no value", e->toChars(), e->type->toChars());
return setError();
}
if (global.params.useTypeInfo && Type::dtypeinfo)
semanticTypeInfo(sc, e->type);
result = e;
}
void visit(AssocArrayLiteralExp *e)
{
if (e->type)
{
result = e;
return;
}
// Run semantic() on each element
bool err_keys = arrayExpressionSemantic(e->keys, sc);
bool err_vals = arrayExpressionSemantic(e->values, sc);
if (err_keys || err_vals)
return setError();
expandTuples(e->keys);
expandTuples(e->values);
if (e->keys->length != e->values->length)
{
e->error("number of keys is %u, must match number of values %u", e->keys->length, e->values->length);
return setError();
}
Type *tkey = NULL;
Type *tvalue = NULL;
err_keys = arrayExpressionToCommonType(sc, e->keys, &tkey);
err_vals = arrayExpressionToCommonType(sc, e->values, &tvalue);
if (err_keys || err_vals)
return setError();
if (tkey == Type::terror || tvalue == Type::terror)
return setError();
e->type = new TypeAArray(tvalue, tkey);
e->type = typeSemantic(e->type, e->loc, sc);
semanticTypeInfo(sc, e->type);
result = e;
}
void visit(StructLiteralExp *e)
{
if (e->type)
{
result = e;
return;
}
e->sd->size(e->loc);
if (e->sd->sizeok != SIZEOKdone)
return setError();
if (arrayExpressionSemantic(e->elements, sc)) // run semantic() on each element
return setError();
expandTuples(e->elements);
/* Fit elements[] to the corresponding type of field[].
*/
if (!e->sd->fit(e->loc, sc, e->elements, e->stype))
return setError();
/* Fill out remainder of elements[] with default initializers for fields[]
*/
if (!e->sd->fill(e->loc, e->elements, false))
{
/* An error in the initializer needs to be recorded as an error
* in the enclosing function or template, since the initializer
* will be part of the stuct declaration.
*/
global.increaseErrorCount();
return setError();
}
if (checkFrameAccess(e->loc, sc, e->sd, e->elements->length))
return setError();
e->type = e->stype ? e->stype : e->sd->type;
result = e;
}
void visit(TypeExp *exp)
{
if (exp->type->ty == Terror)
return setError();
//printf("TypeExp::semantic(%s)\n", exp->type->toChars());
Expression *e;
Type *t;
Dsymbol *s;
exp->type->resolve(exp->loc, sc, &e, &t, &s, true);
if (e)
{
// `(Type)` is actually `(var)` so if `(var)` is a member requiring `this`
// then rewrite as `(this.var)` in case it would be followed by a DotVar
// to fix https://issues.dlang.org/show_bug.cgi?id=9490
VarExp *ve = e->isVarExp();
if (ve && ve->var && exp->parens && !ve->var->isStatic() && !(sc->stc & STCstatic) &&
sc->func && sc->func->needThis() && ve->var->toParent2()->isAggregateDeclaration())
{
// printf("apply fix for issue 9490: add `this.` to `%s`...\n", e->toChars());
e = new DotVarExp(exp->loc, new ThisExp(exp->loc), ve->var, false);
}
//printf("e = %s %s\n", Token::toChars(e->op), e->toChars());
e = expressionSemantic(e, sc);
}
else if (t)
{
//printf("t = %d %s\n", t->ty, t->toChars());
exp->type = typeSemantic(t, exp->loc, sc);
e = exp;
}
else if (s)
{
//printf("s = %s %s\n", s->kind(), s->toChars());
e = resolve(exp->loc, sc, s, true);
}
else
assert(0);
if (global.params.vcomplex)
exp->type->checkComplexTransition(exp->loc);
result = e;
}
void visit(ScopeExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
ScopeDsymbol *sds2 = exp->sds;
TemplateInstance *ti = sds2->isTemplateInstance();
while (ti)
{
WithScopeSymbol *withsym;
if (!ti->findTempDecl(sc, &withsym) ||
!ti->semanticTiargs(sc))
return setError();
if (withsym && withsym->withstate->wthis)
{
Expression *e = new VarExp(exp->loc, withsym->withstate->wthis);
e = new DotTemplateInstanceExp(exp->loc, e, ti);
result = expressionSemantic(e, sc);
return;
}
if (ti->needsTypeInference(sc))
{
if (TemplateDeclaration *td = ti->tempdecl->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)
{
Expression *e = new DotTemplateInstanceExp(exp->loc, new ThisExp(exp->loc), ti->name, ti->tiargs);
result = expressionSemantic(e, sc);
return;
}
}
else if (OverloadSet *os = ti->tempdecl->isOverloadSet())
{
FuncDeclaration *fdthis = hasThis(sc);
AggregateDeclaration *ad = os->parent->isAggregateDeclaration();
if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad)
{
Expression *e = new DotTemplateInstanceExp(exp->loc, new ThisExp(exp->loc), ti->name, ti->tiargs);
result = expressionSemantic(e, sc);
return;
}
}
// ti is an instance which requires IFTI.
exp->sds = ti;
exp->type = Type::tvoid;
result = exp;
return;
}
dsymbolSemantic(ti, sc);
if (!ti->inst || ti->errors)
return setError();
Dsymbol *s = ti->toAlias();
if (s == ti)
{
exp->sds = ti;
exp->type = Type::tvoid;
result = exp;
return;
}
sds2 = s->isScopeDsymbol();
if (sds2)
{
ti = sds2->isTemplateInstance();
//printf("+ sds2 = %s, '%s'\n", sds2->kind(), sds2->toChars());
continue;
}
if (VarDeclaration *v = s->isVarDeclaration())
{
if (!v->type)
{
exp->error("forward reference of %s %s", v->kind(), v->toChars());
return setError();
}
if ((v->storage_class & STCmanifest) && v->_init)
{
/* When an instance that will be converted to a constant exists,
* the instance representation "foo!tiargs" is treated like a
* variable name, and its recursive appearance check (note that
* it's equivalent with a recursive instantiation of foo) is done
* separately from the circular initialization check for the
* eponymous enum variable declaration.
*
* template foo(T) {
* enum bool foo = foo; // recursive definition check (v.inuse)
* }
* template bar(T) {
* enum bool bar = bar!T; // recursive instantiation check (ti.inuse)
* }
*/
if (ti->inuse)
{
exp->error("recursive expansion of %s `%s`", ti->kind(), ti->toPrettyChars());
return setError();
}
Expression *e = v->expandInitializer(exp->loc);
ti->inuse++;
e = expressionSemantic(e, sc);
ti->inuse--;
result = e;
return;
}
}
//printf("s = %s, '%s'\n", s->kind(), s->toChars());
Expression *e = resolve(exp->loc, sc, s, true);
//printf("-1ScopeExp::semantic()\n");
result = e;
return;
}
//printf("sds2 = %s, '%s'\n", sds2->kind(), sds2->toChars());
//printf("\tparent = '%s'\n", sds2->parent->toChars());
dsymbolSemantic(sds2, sc);
if (Type *t = sds2->getType()) // (Aggregate|Enum)Declaration
{
Expression *ex = new TypeExp(exp->loc, t);
result = expressionSemantic(ex, sc);
return;
}
if (TemplateDeclaration *td = sds2->isTemplateDeclaration())
{
result = expressionSemantic(new TemplateExp(exp->loc, td), sc);
return;
}
exp->sds = sds2;
exp->type = Type::tvoid;
//printf("-2ScopeExp::semantic() %s\n", exp->toChars());
result = exp;
}
void visit(NewExp *exp)
{
if (exp->type) // if semantic() already run
{
result = exp;
return;
}
// Bugzilla 11581: With the syntax `new T[edim]` or `thisexp.new T[edim]`,
// T should be analyzed first and edim should go into arguments iff it's
// not a tuple.
Expression *edim = NULL;
if (!exp->arguments && exp->newtype->ty == Tsarray)
{
edim = ((TypeSArray *)exp->newtype)->dim;
exp->newtype = ((TypeNext *)exp->newtype)->next;
}
ClassDeclaration *cdthis = NULL;
if (exp->thisexp)
{
exp->thisexp = expressionSemantic(exp->thisexp, sc);
if (exp->thisexp->op == TOKerror)
return setError();
cdthis = exp->thisexp->type->isClassHandle();
if (!cdthis)
{
exp->error("`this` for nested class must be a class type, not %s", exp->thisexp->type->toChars());
return setError();
}
sc = sc->push(cdthis);
exp->type = typeSemantic(exp->newtype, exp->loc, sc);
sc = sc->pop();
}
else
{
exp->type = typeSemantic(exp->newtype, exp->loc, sc);
}
if (exp->type->ty == Terror)
return setError();
if (edim)
{
if (exp->type->toBasetype()->ty == Ttuple)
{
// --> new T[edim]
exp->type = new TypeSArray(exp->type, edim);
exp->type = typeSemantic(exp->type, exp->loc, sc);
if (exp->type->ty == Terror)
return setError();
}
else
{
// --> new T[](edim)
exp->arguments = new Expressions();
exp->arguments->push(edim);
exp->type = exp->type->arrayOf();
}
}
exp->newtype = exp->type; // in case type gets cast to something else
Type *tb = exp->type->toBasetype();
//printf("tb: %s, deco = %s\n", tb->toChars(), tb->deco);
if (arrayExpressionSemantic(exp->newargs, sc) ||
preFunctionParameters(sc, exp->newargs))
{
return setError();
}
if (arrayExpressionSemantic(exp->arguments, sc) ||
preFunctionParameters(sc, exp->arguments))
{
return setError();
}
if (exp->thisexp && tb->ty != Tclass)
{
exp->error("e.new is only for allocating nested classes, not %s", tb->toChars());
return setError();
}
size_t nargs = exp->arguments ? exp->arguments->length : 0;
Expression *newprefix = NULL;
if (tb->ty == Tclass)
{
ClassDeclaration *cd = ((TypeClass *)tb)->sym;
cd->size(exp->loc);
if (cd->sizeok != SIZEOKdone)
return setError();
if (!cd->ctor)
cd->ctor = cd->searchCtor();
if (cd->noDefaultCtor && !nargs && !cd->defaultCtor)
{
exp->error("default construction is disabled for type %s", cd->type->toChars());
return setError();
}
if (cd->isInterfaceDeclaration())
{
exp->error("cannot create instance of interface %s", cd->toChars());
return setError();
}
if (cd->isAbstract())
{
exp->error("cannot create instance of abstract class %s", cd->toChars());
for (size_t i = 0; i < cd->vtbl.length; i++)
{
FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration();
if (fd && fd->isAbstract())
errorSupplemental(exp->loc, "function `%s` is not implemented", fd->toFullSignature());
}
return setError();
}
// checkDeprecated() is already done in newtype->semantic().
if (cd->isNested())
{
/* We need a 'this' pointer for the nested class.
* Ensure we have the right one.
*/
Dsymbol *s = cd->toParent2();
//printf("cd isNested, parent = %s '%s'\n", s->kind(), s->toPrettyChars());
if (ClassDeclaration *cdn = s->isClassDeclaration())
{
if (!cdthis)
{
// Supply an implicit 'this' and try again
exp->thisexp = new ThisExp(exp->loc);
for (Dsymbol *sp = sc->parent; 1; sp = sp->parent)
{
if (!sp)
{
exp->error("outer class %s `this` needed to `new` nested class %s", cdn->toChars(), cd->toChars());
return setError();
}
ClassDeclaration *cdp = sp->isClassDeclaration();
if (!cdp)
continue;
if (cdp == cdn || cdn->isBaseOf(cdp, NULL))
break;
// Add a '.outer' and try again
exp->thisexp = new DotIdExp(exp->loc, exp->thisexp, Id::outer);
}
exp->thisexp = expressionSemantic(exp->thisexp, sc);
if (exp->thisexp->op == TOKerror)
return setError();
cdthis = exp->thisexp->type->isClassHandle();
}
if (cdthis != cdn && !cdn->isBaseOf(cdthis, NULL))
{
//printf("cdthis = %s\n", cdthis->toChars());
exp->error("`this` for nested class must be of type %s, not %s",
cdn->toChars(), exp->thisexp->type->toChars());
return setError();
}
if (!MODimplicitConv(exp->thisexp->type->mod, exp->newtype->mod))
{
exp->error("nested type %s should have the same or weaker constancy as enclosing type %s",
exp->newtype->toChars(), exp->thisexp->type->toChars());
return setError();
}
}
else if (exp->thisexp)
{
exp->error("e.new is only for allocating nested classes");
return setError();
}
else if (FuncDeclaration *fdn = s->isFuncDeclaration())
{
// make sure the parent context fdn of cd is reachable from sc
if (checkNestedRef(sc->parent, fdn))
{
exp->error("outer function context of %s is needed to `new` nested class %s",
fdn->toPrettyChars(), cd->toPrettyChars());
return setError();
}
}
else
assert(0);
}
else if (exp->thisexp)
{
exp->error("e.new is only for allocating nested classes");
return setError();
}
if (cd->aggNew)
{
// Prepend the size argument to newargs[]
Expression *e = new IntegerExp(exp->loc, cd->size(exp->loc), Type::tsize_t);
if (!exp->newargs)
exp->newargs = new Expressions();
exp->newargs->shift(e);
FuncDeclaration *f = resolveFuncCall(exp->loc, sc, cd->aggNew, NULL, tb, exp->newargs);
if (!f || f->errors)
return setError();
exp->checkDeprecated(sc, f);
exp->checkDisabled(sc, f);
exp->checkPurity(sc, f);
exp->checkSafety(sc, f);
exp->checkNogc(sc, f);
checkAccess(cd, exp->loc, sc, f);
TypeFunction *tf = (TypeFunction *)f->type;
Type *rettype;
if (functionParameters(exp->loc, sc, tf, NULL, exp->newargs, f, &rettype, &newprefix))
return setError();
exp->allocator = f->isNewDeclaration();
assert(exp->allocator);
}
else
{
if (exp->newargs && exp->newargs->length)
{
exp->error("no allocator for %s", cd->toChars());
return setError();
}
}
if (cd->ctor)
{
FuncDeclaration *f = resolveFuncCall(exp->loc, sc, cd->ctor, NULL, tb, exp->arguments, 0);
if (!f || f->errors)
return setError();
exp->checkDeprecated(sc, f);
exp->checkDisabled(sc, f);
exp->checkPurity(sc, f);
exp->checkSafety(sc, f);
exp->checkNogc(sc, f);
checkAccess(cd, exp->loc, sc, f);
TypeFunction *tf = (TypeFunction *)f->type;
if (!exp->arguments)
exp->arguments = new Expressions();
if (functionParameters(exp->loc, sc, tf, exp->type, exp->arguments, f, &exp->type, &exp->argprefix))
return setError();
exp->member = f->isCtorDeclaration();
assert(exp->member);
}
else
{
if (nargs)
{
exp->error("no constructor for %s", cd->toChars());
return setError();
}
// https://issues.dlang.org/show_bug.cgi?id=19941
// Run semantic on all field initializers to resolve any forward
// references. This is the same as done for structs in sd->fill().
for (ClassDeclaration *c = cd; c; c = c->baseClass)
{
for (size_t i = 0; i < c->fields.length; i++)
{
VarDeclaration *v = c->fields[i];
if (v->inuse || v->_scope == NULL || v->_init == NULL ||
v->_init->isVoidInitializer())
continue;
v->inuse++;
v->_init = initializerSemantic(v->_init, v->_scope, v->type, INITinterpret);
v->inuse--;
}
}
}
}
else if (tb->ty == Tstruct)
{
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
sd->size(exp->loc);
if (sd->sizeok != SIZEOKdone)
return setError();
if (!sd->ctor)
sd->ctor = sd->searchCtor();
if (sd->noDefaultCtor && !nargs)
{
exp->error("default construction is disabled for type %s", sd->type->toChars());
return setError();
}
// checkDeprecated() is already done in newtype->semantic().
if (sd->aggNew)
{
// Prepend the uint size argument to newargs[]
Expression *e = new IntegerExp(exp->loc, sd->size(exp->loc), Type::tsize_t);
if (!exp->newargs)
exp->newargs = new Expressions();
exp->newargs->shift(e);
FuncDeclaration *f = resolveFuncCall(exp->loc, sc, sd->aggNew, NULL, tb, exp->newargs);
if (!f || f->errors)
return setError();
exp->checkDeprecated(sc, f);
exp->checkDisabled(sc, f);
exp->checkPurity(sc, f);
exp->checkSafety(sc, f);
exp->checkNogc(sc, f);
checkAccess(sd, exp->loc, sc, f);
TypeFunction *tf = (TypeFunction *)f->type;
Type *rettype;
if (functionParameters(exp->loc, sc, tf, NULL, exp->newargs, f, &rettype, &newprefix))
return setError();
exp->allocator = f->isNewDeclaration();
assert(exp->allocator);
}
else
{
if (exp->newargs && exp->newargs->length)
{
exp->error("no allocator for %s", sd->toChars());
return setError();
}
}
if (sd->ctor && nargs)
{
FuncDeclaration *f = resolveFuncCall(exp->loc, sc, sd->ctor, NULL, tb, exp->arguments, 0);
if (!f || f->errors)
return setError();
exp->checkDeprecated(sc, f);
exp->checkDisabled(sc, f);
exp->checkPurity(sc, f);
exp->checkSafety(sc, f);
exp->checkNogc(sc, f);
checkAccess(sd, exp->loc, sc, f);
TypeFunction *tf = (TypeFunction *)f->type;
if (!exp->arguments)
exp->arguments = new Expressions();
if (functionParameters(exp->loc, sc, tf, exp->type, exp->arguments, f, &exp->type, &exp->argprefix))
return setError();
exp->member = f->isCtorDeclaration();
assert(exp->member);
if (checkFrameAccess(exp->loc, sc, sd, sd->fields.length))
return setError();
}
else
{
if (!exp->arguments)
exp->arguments = new Expressions();
if (!sd->fit(exp->loc, sc, exp->arguments, tb))
return setError();
if (!sd->fill(exp->loc, exp->arguments, false))
return setError();
if (checkFrameAccess(exp->loc, sc, sd, exp->arguments ? exp->arguments->length : 0))
return setError();
}
exp->type = exp->type->pointerTo();
}
else if (tb->ty == Tarray && nargs)
{
Type *tn = tb->nextOf()->baseElemOf();
Dsymbol *s = tn->toDsymbol(sc);
AggregateDeclaration *ad = s ? s->isAggregateDeclaration() : NULL;
if (ad && ad->noDefaultCtor)
{
exp->error("default construction is disabled for type %s", tb->nextOf()->toChars());
return setError();
}
for (size_t i = 0; i < nargs; i++)
{
if (tb->ty != Tarray)
{
exp->error("too many arguments for array");
return setError();
}
Expression *arg = (*exp->arguments)[i];
arg = resolveProperties(sc, arg);
arg = arg->implicitCastTo(sc, Type::tsize_t);
arg = arg->optimize(WANTvalue);
if (arg->op == TOKint64 && (sinteger_t)arg->toInteger() < 0)
{
exp->error("negative array index %s", arg->toChars());
return setError();
}
(*exp->arguments)[i] = arg;
tb = ((TypeDArray *)tb)->next->toBasetype();
}
}
else if (tb->isscalar())
{
if (!nargs)
{
}
else if (nargs == 1)
{
Expression *e = (*exp->arguments)[0];
e = e->implicitCastTo(sc, tb);
(*exp->arguments)[0] = e;
}
else
{
exp->error("more than one argument for construction of %s", exp->type->toChars());
return setError();
}
exp->type = exp->type->pointerTo();
}
else
{
exp->error("new can only create structs, dynamic arrays or class objects, not %s's", exp->type->toChars());
return setError();
}
//printf("NewExp: '%s'\n", toChars());
//printf("NewExp:type '%s'\n", exp->type->toChars());
semanticTypeInfo(sc, exp->type);
if (newprefix)
{
result = Expression::combine(newprefix, exp);
return;
}
result = exp;
}
void visit(NewAnonClassExp *e)
{
Expression *d = new DeclarationExp(e->loc, e->cd);
sc = sc->push(); // just create new scope
sc->flags &= ~SCOPEctfe; // temporary stop CTFE
d = expressionSemantic(d, sc);
sc = sc->pop();
if (!e->cd->errors && sc->intypeof && !sc->parent->inNonRoot())
{
ScopeDsymbol *sds = sc->tinst ? (ScopeDsymbol *)sc->tinst : sc->_module;
sds->members->push(e->cd);
}
Expression *n = new NewExp(e->loc, e->thisexp, e->newargs, e->cd->type, e->arguments);
Expression *c = new CommaExp(e->loc, d, n);
result = expressionSemantic(c, sc);
}
void visit(SymOffExp *e)
{
//dsymbolSemantic(var, sc);
if (!e->type)
e->type = e->var->type->pointerTo();
if (VarDeclaration *v = e->var->isVarDeclaration())
{
if (v->checkNestedReference(sc, e->loc))
return setError();
}
else if (FuncDeclaration *f = e->var->isFuncDeclaration())
{
if (f->checkNestedReference(sc, e->loc))
return setError();
}
result = e;
}
void visit(VarExp *e)
{
VarDeclaration *vd = e->var->isVarDeclaration();
FuncDeclaration *fd = e->var->isFuncDeclaration();
if (fd)
{
//printf("L%d fd = %s\n", __LINE__, f->toChars());
if (!fd->functionSemantic())
return setError();
}
if (!e->type)
e->type = e->var->type;
if (e->type && !e->type->deco)
{
Declaration *decl = e->var->isDeclaration();
if (decl)
decl->inuse++;
e->type = typeSemantic(e->type, e->loc, sc);
if (decl)
decl->inuse--;
}
/* Fix for 1161 doesn't work because it causes protection
* problems when instantiating imported templates passing private
* variables as alias template parameters.
*/
//checkAccess(e->loc, sc, NULL, e->var);
if (vd)
{
if (vd->checkNestedReference(sc, e->loc))
return setError();
// Bugzilla 12025: If the variable is not actually used in runtime code,
// the purity violation error is redundant.
//checkPurity(sc, vd);
}
else if (fd)
{
// TODO: If fd isn't yet resolved its overload, the checkNestedReference
// call would cause incorrect validation.
// Maybe here should be moved in CallExp, or AddrExp for functions.
if (fd->checkNestedReference(sc, e->loc))
return setError();
}
else if (e->var->isOverDeclaration())
{
e->type = Type::tvoid; // ambiguous type?
}
result = e;
}
void visit(TupleExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (exp->e0)
exp->e0 = expressionSemantic(exp->e0, sc);
// Run semantic() on each argument
bool err = false;
for (size_t i = 0; i < exp->exps->length; i++)
{
Expression *e = (*exp->exps)[i];
e = expressionSemantic(e, sc);
if (!e->type)
{
exp->error("%s has no value", e->toChars());
err = true;
}
else if (e->op == TOKerror)
err = true;
else
(*exp->exps)[i] = e;
}
if (err)
return setError();
expandTuples(exp->exps);
exp->type = new TypeTuple(exp->exps);
exp->type = typeSemantic(exp->type, exp->loc, sc);
//printf("-TupleExp::semantic(%s)\n", exp->toChars());
result = exp;
}
void visit(FuncExp *exp)
{
Expression *e = exp;
sc = sc->push(); // just create new scope
sc->flags &= ~SCOPEctfe; // temporary stop CTFE
sc->protection = Prot(Prot::public_); // Bugzilla 12506
if (!exp->type || exp->type == Type::tvoid)
{
/* fd->treq might be incomplete type,
* so should not semantic it.
* void foo(T)(T delegate(int) dg){}
* foo(a=>a); // in IFTI, treq == T delegate(int)
*/
//if (exp->fd->treq)
// exp->fd->treq = typeSemantic(exp->fd->treq, exp->loc, sc);
exp->genIdent(sc);
// Set target of return type inference
if (exp->fd->treq && !exp->fd->type->nextOf())
{
TypeFunction *tfv = NULL;
if (exp->fd->treq->ty == Tdelegate ||
(exp->fd->treq->ty == Tpointer && exp->fd->treq->nextOf()->ty == Tfunction))
tfv = (TypeFunction *)exp->fd->treq->nextOf();
if (tfv)
{
TypeFunction *tfl = (TypeFunction *)exp->fd->type;
tfl->next = tfv->nextOf();
}
}
//printf("td = %p, treq = %p\n", exp->td, exp->fd->treq);
if (exp->td)
{
assert(exp->td->parameters && exp->td->parameters->length);
dsymbolSemantic(exp->td, sc);
exp->type = Type::tvoid; // temporary type
if (exp->fd->treq) // defer type determination
{
FuncExp *fe;
if (exp->matchType(exp->fd->treq, sc, &fe) > MATCHnomatch)
e = fe;
else
e = new ErrorExp();
}
goto Ldone;
}
unsigned olderrors = global.errors;
dsymbolSemantic(exp->fd, sc);
if (olderrors == global.errors)
{
semantic2(exp->fd, sc);
if (olderrors == global.errors)
semantic3(exp->fd, sc);
}
if (olderrors != global.errors)
{
if (exp->fd->type && exp->fd->type->ty == Tfunction && !exp->fd->type->nextOf())
((TypeFunction *)exp->fd->type)->next = Type::terror;
e = new ErrorExp();
goto Ldone;
}
// Type is a "delegate to" or "pointer to" the function literal
if ((exp->fd->isNested() && exp->fd->tok == TOKdelegate) ||
(exp->tok == TOKreserved && exp->fd->treq && exp->fd->treq->ty == Tdelegate))
{
exp->type = new TypeDelegate(exp->fd->type);
exp->type = typeSemantic(exp->type, exp->loc, sc);
exp->fd->tok = TOKdelegate;
}
else
{
exp->type = new TypePointer(exp->fd->type);
exp->type = typeSemantic(exp->type, exp->loc, sc);
//exp->type = exp->fd->type->pointerTo();
/* A lambda expression deduced to function pointer might become
* to a delegate literal implicitly.
*
* auto foo(void function() fp) { return 1; }
* assert(foo({}) == 1);
*
* So, should keep fd->tok == TOKreserve if fd->treq == NULL.
*/
if (exp->fd->treq && exp->fd->treq->ty == Tpointer)
{
// change to non-nested
exp->fd->tok = TOKfunction;
exp->fd->vthis = NULL;
}
}
exp->fd->tookAddressOf++;
}
Ldone:
sc = sc->pop();
result = e;
}
// used from CallExp::semantic()
Expression *callExpSemantic(FuncExp *exp, Scope *sc, Expressions *arguments)
{
if ((!exp->type || exp->type == Type::tvoid) && exp->td && arguments && arguments->length)
{
for (size_t k = 0; k < arguments->length; k++)
{ Expression *checkarg = (*arguments)[k];
if (checkarg->op == TOKerror)
return checkarg;
}
exp->genIdent(sc);
assert(exp->td->parameters && exp->td->parameters->length);
dsymbolSemantic(exp->td, sc);
TypeFunction *tfl = (TypeFunction *)exp->fd->type;
size_t dim = tfl->parameterList.length();
if (arguments->length < dim)
{ // Default arguments are always typed, so they don't need inference.
Parameter *p = tfl->parameterList[arguments->length];
if (p->defaultArg)
dim = arguments->length;
}
if ((tfl->parameterList.varargs == VARARGnone && arguments->length == dim) ||
(tfl->parameterList.varargs != VARARGnone && arguments->length >= dim))
{
Objects *tiargs = new Objects();
tiargs->reserve(exp->td->parameters->length);
for (size_t i = 0; i < exp->td->parameters->length; i++)
{
TemplateParameter *tp = (*exp->td->parameters)[i];
for (size_t u = 0; u < dim; u++)
{ Parameter *p = tfl->parameterList[u];
if (p->type->ty == Tident &&
((TypeIdentifier *)p->type)->ident == tp->ident)
{ Expression *e = (*arguments)[u];
tiargs->push(e->type);
u = dim; // break inner loop
}
}
}
TemplateInstance *ti = new TemplateInstance(exp->loc, exp->td, tiargs);
Expression *se = new ScopeExp(exp->loc, ti);
return expressionSemantic(se, sc);
}
exp->error("cannot infer function literal type");
return new ErrorExp();
}
return expressionSemantic(exp, sc);
}
void visit(DeclarationExp *e)
{
if (e->type)
{
result = e;
return;
}
unsigned olderrors = global.errors;
/* This is here to support extern(linkage) declaration,
* where the extern(linkage) winds up being an AttribDeclaration
* wrapper.
*/
Dsymbol *s = e->declaration;
while (1)
{
AttribDeclaration *ad = s->isAttribDeclaration();
if (ad)
{
if (ad->decl && ad->decl->length == 1)
{
s = (*ad->decl)[0];
continue;
}
}
break;
}
VarDeclaration *v = s->isVarDeclaration();
if (v)
{
// Do semantic() on initializer first, so:
// int a = a;
// will be illegal.
dsymbolSemantic(e->declaration, sc);
s->parent = sc->parent;
}
//printf("inserting '%s' %p into sc = %p\n", s->toChars(), s, sc);
// Insert into both local scope and function scope.
// Must be unique in both.
if (s->ident)
{
if (!sc->insert(s))
{
e->error("declaration %s is already defined", s->toPrettyChars());
return setError();
}
else if (sc->func)
{
// Bugzilla 11720 - include Dataseg variables
if ((s->isFuncDeclaration() ||
s->isAggregateDeclaration() ||
s->isEnumDeclaration() ||
(v && v->isDataseg())) &&
!sc->func->localsymtab->insert(s))
{
e->error("declaration %s is already defined in another scope in %s",
s->toPrettyChars(), sc->func->toChars());
return setError();
}
else
{
// Disallow shadowing
for (Scope *scx = sc->enclosing; scx && (scx->func == sc->func || (scx->func && sc->func->fes)); scx = scx->enclosing)
{
Dsymbol *s2;
if (scx->scopesym && scx->scopesym->symtab &&
(s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL &&
s != s2)
{
// allow STClocal symbols to be shadowed
// TODO: not reallly an optimal design
Declaration *decl = s2->isDeclaration();
if (!decl || !(decl->storage_class & STClocal))
{
if (sc->func->fes)
{
e->deprecation("%s `%s` is shadowing %s `%s`. Rename the `foreach` variable.",
s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars());
}
else
{
e->error("%s %s is shadowing %s %s",
s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars());
return setError();
}
}
}
}
}
}
}
if (!s->isVarDeclaration())
{
Scope *sc2 = sc;
if (sc2->stc & (STCpure | STCnothrow | STCnogc))
sc2 = sc->push();
sc2->stc &= ~(STCpure | STCnothrow | STCnogc);
dsymbolSemantic(e->declaration, sc2);
if (sc2 != sc)
sc2->pop();
s->parent = sc->parent;
}
if (global.errors == olderrors)
{
semantic2(e->declaration, sc);
if (global.errors == olderrors)
{
semantic3(e->declaration, sc);
}
}
// todo: error in declaration should be propagated.
e->type = Type::tvoid;
result = e;
}
void visit(TypeidExp *exp)
{
Type *ta = isType(exp->obj);
Expression *ea = isExpression(exp->obj);
Dsymbol *sa = isDsymbol(exp->obj);
//printf("ta %p ea %p sa %p\n", ta, ea, sa);
if (ta)
{
ta->resolve(exp->loc, sc, &ea, &ta, &sa, true);
}
if (ea)
{
if (Dsymbol *sym = getDsymbol(ea))
ea = resolve(exp->loc, sc, sym, false);
else
ea = expressionSemantic(ea, sc);
ea = resolveProperties(sc, ea);
ta = ea->type;
if (ea->op == TOKtype)
ea = NULL;
}
if (!ta)
{
//printf("ta %p ea %p sa %p\n", ta, ea, sa);
exp->error("no type for typeid(%s)", ea ? ea->toChars() : (sa ? sa->toChars() : ""));
return setError();
}
if (global.params.vcomplex)
ta->checkComplexTransition(exp->loc);
Expression *e;
if (ea && ta->toBasetype()->ty == Tclass)
{
if (!Type::typeinfoclass)
{
error(exp->loc, "`object.TypeInfo_Class` could not be found, but is implicitly used");
e = new ErrorExp();
}
else
{
/* Get the dynamic type, which is .classinfo
*/
ea = expressionSemantic(ea, sc);
e = new TypeidExp(ea->loc, ea);
e->type = Type::typeinfoclass->type;
}
}
else if (ta->ty == Terror)
{
e = new ErrorExp();
}
else
{
// Handle this in the glue layer
e = new TypeidExp(exp->loc, ta);
e->type = getTypeInfoType(exp->loc, ta, sc);
semanticTypeInfo(sc, ta);
if (ea)
{
e = new CommaExp(exp->loc, ea, e); // execute ea
e = expressionSemantic(e, sc);
}
}
result = e;
}
void visit(TraitsExp *e)
{
result = semanticTraits(e, sc);
}
void visit(HaltExp *e)
{
e->type = Type::tnoreturn;
result = e;
}
void visit(IsExp *e)
{
/* is(targ id tok tspec)
* is(targ id : tok2)
* is(targ id == tok2)
*/
//printf("IsExp::semantic(%s)\n", toChars());
if (e->id && !(sc->flags & SCOPEcondition))
{
e->error("can only declare type aliases within static if conditionals or static asserts");
return setError();
}
Type *tded = NULL;
if (e->tok2 == TOKpackage || e->tok2 == TOKmodule) // These is() expressions are special because they can work on modules, not just types.
{
const unsigned oldErrors = global.startGagging();
Dsymbol *sym = e->targ->toDsymbol(sc);
global.endGagging(oldErrors);
if (sym == NULL)
goto Lno;
Package *p = resolveIsPackage(sym);
if (p == NULL)
goto Lno;
if (e->tok2 == TOKpackage && p->isModule()) // Note that isModule() will return null for package modules because they're not actually instances of Module.
goto Lno;
else if(e->tok2 == TOKmodule && !(p->isModule() || p->isPackageMod()))
goto Lno;
tded = e->targ;
goto Lyes;
}
{
Scope *sc2 = sc->copy(); // keep sc->flags
sc2->tinst = NULL;
sc2->minst = NULL;
sc2->flags |= SCOPEfullinst;
Type *t = e->targ->trySemantic(e->loc, sc2);
sc2->pop();
if (!t) // errors, so condition is false
goto Lno;
e->targ = t;
}
if (e->tok2 != TOKreserved)
{
switch (e->tok2)
{
case TOKstruct:
if (e->targ->ty != Tstruct)
goto Lno;
if (((TypeStruct *)e->targ)->sym->isUnionDeclaration())
goto Lno;
tded = e->targ;
break;
case TOKunion:
if (e->targ->ty != Tstruct)
goto Lno;
if (!((TypeStruct *)e->targ)->sym->isUnionDeclaration())
goto Lno;
tded = e->targ;
break;
case TOKclass:
if (e->targ->ty != Tclass)
goto Lno;
if (((TypeClass *)e->targ)->sym->isInterfaceDeclaration())
goto Lno;
tded = e->targ;
break;
case TOKinterface:
if (e->targ->ty != Tclass)
goto Lno;
if (!((TypeClass *)e->targ)->sym->isInterfaceDeclaration())
goto Lno;
tded = e->targ;
break;
case TOKconst:
if (!e->targ->isConst())
goto Lno;
tded = e->targ;
break;
case TOKimmutable:
if (!e->targ->isImmutable())
goto Lno;
tded = e->targ;
break;
case TOKshared:
if (!e->targ->isShared())
goto Lno;
tded = e->targ;
break;
case TOKwild:
if (!e->targ->isWild())
goto Lno;
tded = e->targ;
break;
case TOKsuper:
// If class or interface, get the base class and interfaces
if (e->targ->ty != Tclass)
goto Lno;
else
{
ClassDeclaration *cd = ((TypeClass *)e->targ)->sym;
Parameters *args = new Parameters;
args->reserve(cd->baseclasses->length);
if (cd->semanticRun < PASSsemanticdone)
dsymbolSemantic(cd, NULL);
for (size_t i = 0; i < cd->baseclasses->length; i++)
{
BaseClass *b = (*cd->baseclasses)[i];
args->push(new Parameter(STCin, b->type, NULL, NULL, NULL));
}
tded = new TypeTuple(args);
}
break;
case TOKenum:
if (e->targ->ty != Tenum)
goto Lno;
if (e->id)
tded = ((TypeEnum *)e->targ)->sym->getMemtype(e->loc);
else
tded = e->targ;
if (tded->ty == Terror)
return setError();
break;
case TOKdelegate:
if (e->targ->ty != Tdelegate)
goto Lno;
tded = ((TypeDelegate *)e->targ)->next; // the underlying function type
break;
case TOKfunction:
case TOKparameters:
{
if (e->targ->ty != Tfunction)
goto Lno;
tded = e->targ;
/* Generate tuple from function parameter types.
*/
assert(tded->ty == Tfunction);
TypeFunction *tdedf = (TypeFunction *)tded;
size_t dim = tdedf->parameterList.length();
Parameters *args = new Parameters;
args->reserve(dim);
for (size_t i = 0; i < dim; i++)
{
Parameter *arg = tdedf->parameterList[i];
assert(arg && arg->type);
/* If one of the default arguments was an error,
don't return an invalid tuple
*/
if (e->tok2 == TOKparameters && arg->defaultArg &&
arg->defaultArg->op == TOKerror)
return setError();
args->push(new Parameter(arg->storageClass, arg->type,
(e->tok2 == TOKparameters) ? arg->ident : NULL,
(e->tok2 == TOKparameters) ? arg->defaultArg : NULL,
arg->userAttribDecl));
}
tded = new TypeTuple(args);
break;
}
case TOKreturn:
/* Get the 'return type' for the function,
* delegate, or pointer to function.
*/
if (e->targ->ty == Tfunction)
tded = ((TypeFunction *)e->targ)->next;
else if (e->targ->ty == Tdelegate)
{
tded = ((TypeDelegate *)e->targ)->next;
tded = ((TypeFunction *)tded)->next;
}
else if (e->targ->ty == Tpointer &&
((TypePointer *)e->targ)->next->ty == Tfunction)
{
tded = ((TypePointer *)e->targ)->next;
tded = ((TypeFunction *)tded)->next;
}
else
goto Lno;
break;
case TOKargTypes:
/* Generate a type tuple of the equivalent types used to determine if a
* function argument of this type can be passed in registers.
* The results of this are highly platform dependent, and intended
* primarly for use in implementing va_arg().
*/
tded = target.toArgTypes(e->targ);
if (!tded)
goto Lno; // not valid for a parameter
break;
case TOKvector:
if (e->targ->ty != Tvector)
goto Lno;
tded = ((TypeVector *)e->targ)->basetype;
break;
default:
assert(0);
}
goto Lyes;
}
else if (e->tspec && !e->id && !(e->parameters && e->parameters->length))
{
/* Evaluate to true if targ matches tspec
* is(targ == tspec)
* is(targ : tspec)
*/
e->tspec = typeSemantic(e->tspec, e->loc, sc);
//printf("targ = %s, %s\n", e->targ->toChars(), e->targ->deco);
//printf("tspec = %s, %s\n", e->tspec->toChars(), e->tspec->deco);
if (e->tok == TOKcolon)
{
if (e->targ->implicitConvTo(e->tspec))
goto Lyes;
else
goto Lno;
}
else /* == */
{
if (e->targ->equals(e->tspec))
goto Lyes;
else
goto Lno;
}
}
else if (e->tspec)
{
/* Evaluate to true if targ matches tspec.
* If true, declare id as an alias for the specialized type.
* is(targ == tspec, tpl)
* is(targ : tspec, tpl)
* is(targ id == tspec)
* is(targ id : tspec)
* is(targ id == tspec, tpl)
* is(targ id : tspec, tpl)
*/
Identifier *tid = e->id ? e->id : Identifier::generateId("__isexp_id");
e->parameters->insert(0, new TemplateTypeParameter(e->loc, tid, NULL, NULL));
Objects dedtypes;
dedtypes.setDim(e->parameters->length);
dedtypes.zero();
MATCH m = deduceType(e->targ, sc, e->tspec, e->parameters, &dedtypes);
//printf("targ: %s\n", e->targ->toChars());
//printf("tspec: %s\n", e->tspec->toChars());
if (m <= MATCHnomatch ||
(m != MATCHexact && e->tok == TOKequal))
{
goto Lno;
}
else
{
tded = (Type *)dedtypes[0];
if (!tded)
tded = e->targ;
Objects tiargs;
tiargs.setDim(1);
tiargs[0] = e->targ;
/* Declare trailing parameters
*/
for (size_t i = 1; i < e->parameters->length; i++)
{
TemplateParameter *tp = (*e->parameters)[i];
Declaration *s = NULL;
m = tp->matchArg(e->loc, sc, &tiargs, i, e->parameters, &dedtypes, &s);
if (m <= MATCHnomatch)
goto Lno;
dsymbolSemantic(s, sc);
if (!sc->insert(s))
e->error("declaration %s is already defined", s->toChars());
unSpeculative(sc, s);
}
goto Lyes;
}
}
else if (e->id)
{
/* Declare id as an alias for type targ. Evaluate to true
* is(targ id)
*/
tded = e->targ;
goto Lyes;
}
Lyes:
if (e->id)
{
Dsymbol *s;
Tuple *tup = isTuple(tded);
if (tup)
s = new TupleDeclaration(e->loc, e->id, &(tup->objects));
else
s = new AliasDeclaration(e->loc, e->id, tded);
dsymbolSemantic(s, sc);
/* The reason for the !tup is unclear. It fails Phobos unittests if it is not there.
* More investigation is needed.
*/
if (!tup && !sc->insert(s))
e->error("declaration %s is already defined", s->toChars());
unSpeculative(sc, s);
}
//printf("Lyes\n");
result = new IntegerExp(e->loc, 1, Type::tbool);
return;
Lno:
//printf("Lno\n");
result = new IntegerExp(e->loc, 0, Type::tbool);
}
void visit(BinAssignExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->e1->checkReadModifyWrite(exp->op, exp->e2))
return setError();
if (exp->e1->op == TOKarraylength)
{
// arr.length op= e2;
e = rewriteOpAssign(exp);
e = expressionSemantic(e, sc);
result = e;
return;
}
if (exp->e1->op == TOKslice || exp->e1->type->ty == Tarray || exp->e1->type->ty == Tsarray)
{
if (checkNonAssignmentArrayOp(exp->e1))
return setError();
if (exp->e1->op == TOKslice)
((SliceExp *)exp->e1)->arrayop = true;
// T[] op= ...
if (exp->e2->implicitConvTo(exp->e1->type->nextOf()))
{
// T[] op= T
exp->e2 = exp->e2->castTo(sc, exp->e1->type->nextOf());
}
else if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
exp->type = exp->e1->type;
result = arrayOp(exp, sc);
return;
}
exp->e1 = expressionSemantic(exp->e1, sc);
exp->e1 = exp->e1->optimize(WANTvalue);
exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
exp->type = exp->e1->type;
if (exp->checkScalar())
return setError();
int arith = (exp->op == TOKaddass || exp->op == TOKminass || exp->op == TOKmulass ||
exp->op == TOKdivass || exp->op == TOKmodass || exp->op == TOKpowass);
int bitwise = (exp->op == TOKandass || exp->op == TOKorass || exp->op == TOKxorass);
int shift = (exp->op == TOKshlass || exp->op == TOKshrass || exp->op == TOKushrass);
if (bitwise && exp->type->toBasetype()->ty == Tbool)
exp->e2 = exp->e2->implicitCastTo(sc, exp->type);
else if (exp->checkNoBool())
return setError();
if ((exp->op == TOKaddass || exp->op == TOKminass) &&
exp->e1->type->toBasetype()->ty == Tpointer &&
exp->e2->type->toBasetype()->isintegral())
{
result = scaleFactor(exp, sc);
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
if (arith && exp->checkArithmeticBin())
return setError();
if ((bitwise || shift) && exp->checkIntegralBin())
return setError();
if (shift)
{
if (exp->e2->type->toBasetype()->ty != Tvector)
exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
}
if (!target.isVectorOpSupported(exp->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
if (exp->e1->op == TOKerror || exp->e2->op == TOKerror)
return setError();
e = exp->checkOpAssignTypes(sc);
if (e->op == TOKerror)
{
result = e;
return;
}
assert(e->op == TOKassign || e == exp);
result = ((BinExp *)e)->reorderSettingAAElem(sc);
}
private:
Expression *compileIt(CompileExp *exp)
{
OutBuffer buf;
if (expressionsToString(buf, sc, exp->exps))
return NULL;
unsigned errors = global.errors;
const size_t len = buf.length();
const char *str = buf.extractChars();
Parser p(exp->loc, sc->_module, (const utf8_t *)str, len, false);
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);
Expression *e = p.parseExpression();
if (global.errors != errors)
return NULL;
if (p.token.value != TOKeof)
{
exp->error("incomplete mixin expression (%s)", str);
return NULL;
}
return e;
}
public:
void visit(CompileExp *exp)
{
//printf("CompileExp::semantic('%s')\n", exp->toChars());
Expression *e = compileIt(exp);
if (!e)
return setError();
result = expressionSemantic(e, sc);
}
void visit(ImportExp *e)
{
StringExp *se = semanticString(sc, e->e1, "file name argument");
if (!se)
return setError();
se = se->toUTF8(sc);
const char *name = (char *)se->string;
if (!global.params.fileImppath)
{
e->error("need -Jpath switch to import text file %s", name);
return setError();
}
/* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
* ('Path Traversal') attacks.
* http://cwe.mitre.org/data/definitions/22.html
*/
name = FileName::safeSearchPath(global.filePath, name);
if (!name)
{
e->error("file %s cannot be found or not in a path specified with -J", se->toChars());
return setError();
}
sc->_module->contentImportedFiles.push(name);
if (global.params.verbose)
message("file %.*s\t(%s)", (int)se->len, (char *)se->string, name);
if (global.params.moduleDeps != NULL)
{
OutBuffer *ob = global.params.moduleDeps;
Module* imod = sc->instantiatingModule();
if (!global.params.moduleDepsFile.length)
ob->writestring("depsFile ");
ob->writestring(imod->toPrettyChars());
ob->writestring(" (");
escapePath(ob, imod->srcfile->toChars());
ob->writestring(") : ");
if (global.params.moduleDepsFile.length)
ob->writestring("string : ");
ob->writestring((char *) se->string);
ob->writestring(" (");
escapePath(ob, name);
ob->writestring(")");
ob->writenl();
}
{
File f(name);
if (f.read())
{
e->error("cannot read file %s", f.toChars());
return setError();
}
else
{
f.ref = 1;
se = new StringExp(e->loc, f.buffer, f.len);
}
}
result = expressionSemantic(se, sc);
}
void visit(AssertExp *exp)
{
if (Expression *ex = unaSemantic(exp, sc))
{
result = ex;
return;
}
exp->e1 = resolveProperties(sc, exp->e1);
// BUG: see if we can do compile time elimination of the Assert
exp->e1 = exp->e1->optimize(WANTvalue);
exp->e1 = exp->e1->toBoolean(sc);
if (exp->msg)
{
exp->msg = expressionSemantic(exp->msg, sc);
exp->msg = resolveProperties(sc, exp->msg);
exp->msg = exp->msg->implicitCastTo(sc, Type::tchar->constOf()->arrayOf());
exp->msg = exp->msg->optimize(WANTvalue);
}
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
if (exp->msg && exp->msg->op == TOKerror)
{
result = exp->msg;
return;
}
bool f1 = checkNonAssignmentArrayOp(exp->e1);
bool f2 = exp->msg && checkNonAssignmentArrayOp(exp->msg);
if (f1 || f2)
return setError();
if (exp->e1->isBool(false))
{
/* This is an `assert(0)` which means halt program execution
*/
FuncDeclaration *fd = sc->parent->isFuncDeclaration();
if (fd)
fd->hasReturnExp |= 4;
sc->callSuper |= CSXhalt;
if (sc->fieldinit)
{
for (size_t i = 0; i < sc->fieldinit_dim; i++)
sc->fieldinit[i] |= CSXhalt;
}
if (global.params.useAssert == CHECKENABLEoff)
{
Expression *e = new HaltExp(exp->loc);
e = expressionSemantic(e, sc);
result = e;
return;
}
exp->type = Type::tnoreturn;
}
else
exp->type = Type::tvoid;
result = exp;
}
void visit(DotIdExp *exp)
{
Expression *e = semanticY(exp, sc, 1);
if (e && isDotOpDispatch(e))
{
unsigned errors = global.startGagging();
e = resolvePropertiesX(sc, e);
if (global.endGagging(errors))
e = NULL; /* fall down to UFCS */
else
{
result = e;
return;
}
}
if (!e) // if failed to find the property
{
/* If ident is not a valid property, rewrite:
* e1.ident
* as:
* .ident(e1)
*/
e = resolveUFCSProperties(sc, exp);
}
result = e;
}
void visit(DotTemplateExp *e)
{
if (e->type)
{
result = e;
return;
}
if (Expression *ex = unaSemantic(e, sc))
{
result = ex;
return;
}
// 'void' like TemplateExp
e->type = Type::tvoid;
result = e;
}
void visit(DotVarExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
exp->var = exp->var->toAlias()->isDeclaration();
exp->e1 = expressionSemantic(exp->e1, sc);
if (TupleDeclaration *tup = exp->var->isTupleDeclaration())
{
/* Replace:
* e1.tuple(a, b, c)
* with:
* tuple(e1.a, e1.b, e1.c)
*/
Expression *e0 = NULL;
Expression *ev = sc->func ? extractSideEffect(sc, "__tup", &e0, exp->e1) : exp->e1;
Expressions *exps = new Expressions;
exps->reserve(tup->objects->length);
for (size_t i = 0; i < tup->objects->length; i++)
{
RootObject *o = (*tup->objects)[i];
Expression *e;
if (o->dyncast() == DYNCAST_EXPRESSION)
{
e = (Expression *)o;
if (e->op == TOKdsymbol)
{
Dsymbol *s = ((DsymbolExp *)e)->s;
e = new DotVarExp(exp->loc, ev, s->isDeclaration());
}
}
else if (o->dyncast() == DYNCAST_DSYMBOL)
{
e = new DsymbolExp(exp->loc, (Dsymbol *)o);
}
else if (o->dyncast() == DYNCAST_TYPE)
{
e = new TypeExp(exp->loc, (Type *)o);
}
else
{
exp->error("%s is not an expression", o->toChars());
return setError();
}
exps->push(e);
}
Expression *e = new TupleExp(exp->loc, e0, exps);
e = expressionSemantic(e, sc);
result = e;
return;
}
exp->e1 = exp->e1->addDtorHook(sc);
Type *t1 = exp->e1->type;
if (FuncDeclaration *fd = exp->var->isFuncDeclaration())
{
// for functions, do checks after overload resolution
if (!fd->functionSemantic())
return setError();
/* Bugzilla 13843: If fd obviously has no overloads, we should
* normalize AST, and it will give a chance to wrap fd with FuncExp.
*/
if (fd->isNested() || fd->isFuncLiteralDeclaration())
{
// (e1, fd)
Expression *e = resolve(exp->loc, sc, fd, false);
result = Expression::combine(exp->e1, e);
return;
}
exp->type = fd->type;
assert(exp->type);
}
else if (exp->var->isOverDeclaration())
{
exp->type = Type::tvoid; // ambiguous type?
}
else
{
exp->type = exp->var->type;
if (!exp->type && global.errors)
{
// var is goofed up, just return 0
return setError();
}
assert(exp->type);
if (t1->ty == Tpointer)
t1 = t1->nextOf();
exp->type = exp->type->addMod(t1->mod);
Dsymbol *vparent = exp->var->toParent();
AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL;
if (Expression *e1x = getRightThis(exp->loc, sc, ad, exp->e1, exp->var, 1))
exp->e1 = e1x;
else
{
/* Later checkRightThis will report correct error for invalid field variable access.
*/
Expression *e = new VarExp(exp->loc, exp->var);
e = expressionSemantic(e, sc);
result = e;
return;
}
checkAccess(exp->loc, sc, exp->e1, exp->var);
VarDeclaration *v = exp->var->isVarDeclaration();
if (v && (v->isDataseg() || (v->storage_class & STCmanifest)))
{
Expression *e = expandVar(WANTvalue, v);
if (e)
{
result = e;
return;
}
}
if (v && v->isDataseg()) // fix bugzilla 8238
{
// (e1, v)
checkAccess(exp->loc, sc, exp->e1, v);
Expression *e = new VarExp(exp->loc, v);
e = new CommaExp(exp->loc, exp->e1, e);
e = expressionSemantic(e, sc);
result = e;
return;
}
}
//printf("-DotVarExp::semantic('%s')\n", exp->toChars());
result = exp;
}
void visit(DotTemplateInstanceExp *exp)
{
// Indicate we need to resolve by UFCS.
Expression *e = semanticY(exp, sc, 1);
if (!e)
e = resolveUFCSProperties(sc, exp);
result = e;
}
void visit(DelegateExp *e)
{
if (e->type)
{
result = e;
return;
}
e->e1 = expressionSemantic(e->e1, sc);
e->type = new TypeDelegate(e->func->type);
e->type = typeSemantic(e->type, e->loc, sc);
FuncDeclaration *f = e->func->toAliasFunc();
AggregateDeclaration *ad = f->toParent()->isAggregateDeclaration();
if (f->needThis())
e->e1 = getRightThis(e->loc, sc, ad, e->e1, f);
if (f->type->ty == Tfunction)
{
TypeFunction *tf = (TypeFunction *)f->type;
if (!MODmethodConv(e->e1->type->mod, f->type->mod))
{
OutBuffer thisBuf, funcBuf;
MODMatchToBuffer(&thisBuf, e->e1->type->mod, tf->mod);
MODMatchToBuffer(&funcBuf, tf->mod, e->e1->type->mod);
e->error("%smethod %s is not callable using a %s%s",
funcBuf.peekChars(), f->toPrettyChars(), thisBuf.peekChars(), e->e1->toChars());
return setError();
}
}
if (ad && ad->isClassDeclaration() && ad->type != e->e1->type)
{
// A downcast is required for interfaces, see Bugzilla 3706
e->e1 = new CastExp(e->loc, e->e1, ad->type);
e->e1 = expressionSemantic(e->e1, sc);
}
result = e;
}
void visit(DotTypeExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *e = unaSemantic(exp, sc))
{
result = e;
return;
}
exp->type = exp->sym->getType()->addMod(exp->e1->type->mod);
result = exp;
}
void visit(CallExp *exp)
{
if (exp->type)
{
result = exp; // semantic() already run
return;
}
Type *t1;
Objects *tiargs = NULL; // initial list of template arguments
Expression *ethis = NULL;
Type *tthis = NULL;
Expression *e1org = exp->e1;
if (exp->e1->op == TOKcomma)
{
/* Rewrite (a,b)(args) as (a,(b(args)))
*/
CommaExp *ce = (CommaExp *)exp->e1;
exp->e1 = ce->e2;
ce->e2 = exp;
result = expressionSemantic(ce, sc);
return;
}
if (exp->e1->op == TOKdelegate)
{
DelegateExp *de = (DelegateExp *)exp->e1;
exp->e1 = new DotVarExp(de->loc, de->e1, de->func, de->hasOverloads);
result = expressionSemantic(exp, sc);
return;
}
if (exp->e1->op == TOKfunction)
{
if (arrayExpressionSemantic(exp->arguments, sc) ||
preFunctionParameters(sc, exp->arguments))
{
return setError();
}
// Run e1 semantic even if arguments have any errors
FuncExp *fe = (FuncExp *)exp->e1;
exp->e1 = callExpSemantic(fe, sc, exp->arguments);
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
}
if (Expression *ex = resolveUFCS(sc, exp))
{
result = ex;
return;
}
/* This recognizes:
* foo!(tiargs)(funcargs)
*/
if (exp->e1->op == TOKscope)
{
ScopeExp *se = (ScopeExp *)exp->e1;
TemplateInstance *ti = se->sds->isTemplateInstance();
if (ti)
{
/* Attempt to instantiate ti. If that works, go with it.
* If not, go with partial explicit specialization.
*/
WithScopeSymbol *withsym;
if (!ti->findTempDecl(sc, &withsym) ||
!ti->semanticTiargs(sc))
{
return setError();
}
if (withsym && withsym->withstate->wthis)
{
exp->e1 = new VarExp(exp->e1->loc, withsym->withstate->wthis);
exp->e1 = new DotTemplateInstanceExp(exp->e1->loc, exp->e1, ti);
goto Ldotti;
}
if (ti->needsTypeInference(sc, 1))
{
/* Go with partial explicit specialization
*/
tiargs = ti->tiargs;
assert(ti->tempdecl);
if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration())
exp->e1 = new TemplateExp(exp->loc, td);
else if (OverDeclaration *od = ti->tempdecl->isOverDeclaration())
exp->e1 = new VarExp(exp->loc, od);
else
exp->e1 = new OverExp(exp->loc, ti->tempdecl->isOverloadSet());
}
else
{
Expression *e1x = expressionSemantic(exp->e1, sc);
if (e1x->op == TOKerror)
{
result = e1x;
return;
}
exp->e1 = e1x;
}
}
}
/* This recognizes:
* expr.foo!(tiargs)(funcargs)
*/
Ldotti:
if (exp->e1->op == TOKdotti && !exp->e1->type)
{
DotTemplateInstanceExp *se = (DotTemplateInstanceExp *)exp->e1;
TemplateInstance *ti = se->ti;
{
/* Attempt to instantiate ti. If that works, go with it.
* If not, go with partial explicit specialization.
*/
if (!se->findTempDecl(sc) ||
!ti->semanticTiargs(sc))
{
return setError();
}
if (ti->needsTypeInference(sc, 1))
{
/* Go with partial explicit specialization
*/
tiargs = ti->tiargs;
assert(ti->tempdecl);
if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration())
exp->e1 = new DotTemplateExp(exp->loc, se->e1, td);
else if (OverDeclaration *od = ti->tempdecl->isOverDeclaration())
{
exp->e1 = new DotVarExp(exp->loc, se->e1, od, true);
}
else
exp->e1 = new DotExp(exp->loc, se->e1, new OverExp(exp->loc, ti->tempdecl->isOverloadSet()));
}
else
{
Expression *e1x = expressionSemantic(exp->e1, sc);
if (e1x->op == TOKerror)
{
result =e1x;
return;
}
exp->e1 = e1x;
}
}
}
Lagain:
//printf("Lagain: %s\n", exp->toChars());
exp->f = NULL;
if (exp->e1->op == TOKthis || exp->e1->op == TOKsuper)
{
// semantic() run later for these
}
else
{
if (exp->e1->op == TOKdotid)
{
DotIdExp *die = (DotIdExp *)exp->e1;
exp->e1 = expressionSemantic(die, sc);
/* Look for e1 having been rewritten to expr.opDispatch!(string)
* We handle such earlier, so go back.
* Note that in the rewrite, we carefully did not run semantic() on e1
*/
if (exp->e1->op == TOKdotti && !exp->e1->type)
{
goto Ldotti;
}
}
else
{
static int nest;
if (++nest > global.recursionLimit)
{
exp->error("recursive evaluation of %s", exp->toChars());
--nest;
return setError();
}
Expression *ex = unaSemantic(exp, sc);
--nest;
if (ex)
{
result = ex;
return;
}
}
/* Look for e1 being a lazy parameter
*/
if (exp->e1->op == TOKvar)
{
VarExp *ve = (VarExp *)exp->e1;
if (ve->var->storage_class & STClazy)
{
// lazy paramaters can be called without violating purity and safety
Type *tw = ve->var->type;
Type *tc = ve->var->type->substWildTo(MODconst);
TypeFunction *tf = new TypeFunction(ParameterList(), tc, LINKd, STCsafe | STCpure);
(tf = (TypeFunction *)typeSemantic(tf, exp->loc, sc))->next = tw; // hack for bug7757
TypeDelegate *t = new TypeDelegate(tf);
ve->type = typeSemantic(t, exp->loc, sc);
}
VarDeclaration *v = ve->var->isVarDeclaration();
if (v && ve->checkPurity(sc, v))
return setError();
}
if (exp->e1->op == TOKsymoff && ((SymOffExp *)exp->e1)->hasOverloads)
{
SymOffExp *se = (SymOffExp *)exp->e1;
exp->e1 = new VarExp(se->loc, se->var, true);
exp->e1 = expressionSemantic(exp->e1, sc);
}
else if (exp->e1->op == TOKdot)
{
DotExp *de = (DotExp *) exp->e1;
if (de->e2->op == TOKoverloadset)
{
ethis = de->e1;
tthis = de->e1->type;
exp->e1 = de->e2;
}
}
else if (exp->e1->op == TOKstar && exp->e1->type->ty == Tfunction)
{
// Rewrite (*fp)(arguments) to fp(arguments)
exp->e1 = ((PtrExp *)exp->e1)->e1;
}
}
t1 = exp->e1->type ? exp->e1->type->toBasetype() : NULL;
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
if (arrayExpressionSemantic(exp->arguments, sc) ||
preFunctionParameters(sc, exp->arguments))
{
return setError();
}
// Check for call operator overload
if (t1)
{
if (t1->ty == Tstruct)
{
StructDeclaration *sd = ((TypeStruct *)t1)->sym;
sd->size(exp->loc); // Resolve forward references to construct object
if (sd->sizeok != SIZEOKdone)
return setError();
if (!sd->ctor)
sd->ctor = sd->searchCtor();
// First look for constructor
if (exp->e1->op == TOKtype && sd->ctor)
{
if (!sd->noDefaultCtor && !(exp->arguments && exp->arguments->length))
goto Lx;
StructLiteralExp *sle = new StructLiteralExp(exp->loc, sd, NULL, exp->e1->type);
if (!sd->fill(exp->loc, sle->elements, true))
return setError();
if (checkFrameAccess(exp->loc, sc, sd, sle->elements->length))
return setError();
// Bugzilla 14556: Set concrete type to avoid further redundant semantic().
sle->type = exp->e1->type;
/* Constructor takes a mutable object, so don't use
* the immutable initializer symbol.
*/
sle->useStaticInit = false;
Expression *e = sle;
if (CtorDeclaration *cf = sd->ctor->isCtorDeclaration())
{
e = new DotVarExp(exp->loc, e, cf, true);
}
else if (TemplateDeclaration *td = sd->ctor->isTemplateDeclaration())
{
e = new DotTemplateExp(exp->loc, e, td);
}
else if (OverloadSet *os = sd->ctor->isOverloadSet())
{
e = new DotExp(exp->loc, e, new OverExp(exp->loc, os));
}
else
assert(0);
e = new CallExp(exp->loc, e, exp->arguments);
result = expressionSemantic(e, sc);
return;
}
// No constructor, look for overload of opCall
if (search_function(sd, Id::call))
goto L1; // overload of opCall, therefore it's a call
if (exp->e1->op != TOKtype)
{
if (sd->aliasthis && exp->e1->type != exp->att1)
{
if (!exp->att1 && exp->e1->type->checkAliasThisRec())
exp->att1 = exp->e1->type;
exp->e1 = resolveAliasThis(sc, exp->e1);
goto Lagain;
}
exp->error("%s %s does not overload ()", sd->kind(), sd->toChars());
return setError();
}
/* It's a struct literal
*/
Lx:
Expression *e = new StructLiteralExp(exp->loc, sd, exp->arguments, exp->e1->type);
result = expressionSemantic(e, sc);
return;
}
else if (t1->ty == Tclass)
{
L1:
// Rewrite as e1.call(arguments)
Expression *e = new DotIdExp(exp->loc, exp->e1, Id::call);
e = new CallExp(exp->loc, e, exp->arguments);
result = expressionSemantic(e, sc);
return;
}
else if (exp->e1->op == TOKtype && t1->isscalar())
{
Expression *e;
// Make sure to use the the enum type itself rather than its
// base type (see bugzilla 16346)
if (exp->e1->type->ty == Tenum)
{
t1 = exp->e1->type;
}
if (!exp->arguments || exp->arguments->length == 0)
{
e = t1->defaultInitLiteral(exp->loc);
}
else if (exp->arguments->length == 1)
{
e = (*exp->arguments)[0];
e = e->implicitCastTo(sc, t1);
e = new CastExp(exp->loc, e, t1);
}
else
{
exp->error("more than one argument for construction of %s", t1->toChars());
e = new ErrorExp();
}
result = expressionSemantic(e, sc);
return;
}
}
if ((exp->e1->op == TOKdotvar && t1->ty == Tfunction) ||
exp->e1->op == TOKdottd)
{
UnaExp *ue = (UnaExp *)(exp->e1);
Expression *ue1 = ue->e1;
Expression *ue1old = ue1; // need for 'right this' check
VarDeclaration *v;
if (ue1->op == TOKvar &&
(v = ((VarExp *)ue1)->var->isVarDeclaration()) != NULL &&
v->needThis())
{
ue->e1 = new TypeExp(ue1->loc, ue1->type);
ue1 = NULL;
}
DotVarExp *dve;
DotTemplateExp *dte;
Dsymbol *s;
if (exp->e1->op == TOKdotvar)
{
dve = (DotVarExp *)(exp->e1);
dte = NULL;
s = dve->var;
tiargs = NULL;
}
else
{
dve = NULL;
dte = (DotTemplateExp *)(exp->e1);
s = dte->td;
}
// Do overload resolution
exp->f = resolveFuncCall(exp->loc, sc, s, tiargs, ue1 ? ue1->type : NULL, exp->arguments);
if (!exp->f || exp->f->errors || exp->f->type->ty == Terror)
return setError();
if (exp->f->interfaceVirtual)
{
/* Cast 'this' to the type of the interface, and replace f with the interface's equivalent
*/
BaseClass *b = exp->f->interfaceVirtual;
ClassDeclaration *ad2 = b->sym;
ue->e1 = ue->e1->castTo(sc, ad2->type->addMod(ue->e1->type->mod));
ue->e1 = expressionSemantic(ue->e1, sc);
ue1 = ue->e1;
int vi = exp->f->findVtblIndex((Dsymbols*)&ad2->vtbl, (int)ad2->vtbl.length);
assert(vi >= 0);
exp->f = ad2->vtbl[vi]->isFuncDeclaration();
assert(exp->f);
}
if (exp->f->needThis())
{
AggregateDeclaration *ad = exp->f->toParent2()->isAggregateDeclaration();
ue->e1 = getRightThis(exp->loc, sc, ad, ue->e1, exp->f);
if (ue->e1->op == TOKerror)
{
result = ue->e1;
return;
}
ethis = ue->e1;
tthis = ue->e1->type;
if (!(exp->f->type->ty == Tfunction && ((TypeFunction *)exp->f->type)->isscope))
{
if (global.params.vsafe && checkParamArgumentEscape(sc, exp->f, Id::This, ethis, false))
return setError();
}
}
/* Cannot call public functions from inside invariant
* (because then the invariant would have infinite recursion)
*/
if (sc->func && sc->func->isInvariantDeclaration() &&
ue->e1->op == TOKthis &&
exp->f->addPostInvariant()
)
{
exp->error("cannot call public/export function %s from invariant", exp->f->toChars());
return setError();
}
exp->checkDeprecated(sc, exp->f);
exp->checkDisabled(sc, exp->f);
exp->checkPurity(sc, exp->f);
exp->checkSafety(sc, exp->f);
exp->checkNogc(sc, exp->f);
checkAccess(exp->loc, sc, ue->e1, exp->f);
if (!exp->f->needThis())
{
exp->e1 = Expression::combine(ue->e1, new VarExp(exp->loc, exp->f, false));
}
else
{
if (ue1old->checkRightThis(sc))
return setError();
if (exp->e1->op == TOKdotvar)
{
dve->var = exp->f;
exp->e1->type = exp->f->type;
}
else
{
exp->e1 = new DotVarExp(exp->loc, dte->e1, exp->f, false);
exp->e1 = expressionSemantic(exp->e1, sc);
if (exp->e1->op == TOKerror)
return setError();
ue = (UnaExp *)exp->e1;
}
// See if we need to adjust the 'this' pointer
AggregateDeclaration *ad = exp->f->isThis();
ClassDeclaration *cd = ue->e1->type->isClassHandle();
if (ad && cd && ad->isClassDeclaration())
{
if (ue->e1->op == TOKdottype)
{
ue->e1 = ((DotTypeExp *)ue->e1)->e1;
exp->directcall = true;
}
else if (ue->e1->op == TOKsuper)
exp->directcall = true;
else if ((cd->storage_class & STCfinal) != 0) // Bugzilla 14211
exp->directcall = true;
if (ad != cd)
{
ue->e1 = ue->e1->castTo(sc, ad->type->addMod(ue->e1->type->mod));
ue->e1 = expressionSemantic(ue->e1, sc);
}
}
}
// If we've got a pointer to a function then deference it
// https://issues.dlang.org/show_bug.cgi?id=16483
if (exp->e1->type->ty == Tpointer && exp->e1->type->nextOf()->ty == Tfunction)
{
Expression *e = new PtrExp(exp->loc, exp->e1);
e->type = exp->e1->type->nextOf();
exp->e1 = e;
}
t1 = exp->e1->type;
}
else if (exp->e1->op == TOKsuper)
{
// Base class constructor call
AggregateDeclaration *ad = sc->func ? sc->func->isThis() : NULL;
ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL;
if (!cd || !cd->baseClass || !sc->func->isCtorDeclaration())
{
exp->error("super class constructor call must be in a constructor");
return setError();
}
if (!cd->baseClass->ctor)
{
exp->error("no super class constructor for %s", cd->baseClass->toChars());
return setError();
}
if (!sc->intypeof && !(sc->callSuper & CSXhalt))
{
if (sc->noctor || sc->callSuper & CSXlabel)
exp->error("constructor calls not allowed in loops or after labels");
if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
exp->error("multiple constructor calls");
if ((sc->callSuper & CSXreturn) && !(sc->callSuper & CSXany_ctor))
exp->error("an earlier return statement skips constructor");
sc->callSuper |= CSXany_ctor | CSXsuper_ctor;
}
tthis = cd->type->addMod(sc->func->type->mod);
if (OverloadSet *os = cd->baseClass->ctor->isOverloadSet())
exp->f = resolveOverloadSet(exp->loc, sc, os, NULL, tthis, exp->arguments);
else
exp->f = resolveFuncCall(exp->loc, sc, cd->baseClass->ctor, NULL, tthis, exp->arguments, 0);
if (!exp->f || exp->f->errors)
return setError();
exp->checkDeprecated(sc, exp->f);
exp->checkDisabled(sc, exp->f);
exp->checkPurity(sc, exp->f);
exp->checkSafety(sc, exp->f);
exp->checkNogc(sc, exp->f);
checkAccess(exp->loc, sc, NULL, exp->f);
exp->e1 = new DotVarExp(exp->e1->loc, exp->e1, exp->f, false);
exp->e1 = expressionSemantic(exp->e1, sc);
t1 = exp->e1->type;
}
else if (exp->e1->op == TOKthis)
{
// same class constructor call
AggregateDeclaration *ad = sc->func ? sc->func->isThis() : NULL;
if (!ad || !sc->func->isCtorDeclaration())
{
exp->error("constructor call must be in a constructor");
return setError();
}
// https://issues.dlang.org/show_bug.cgi?id=18719
// If `exp` is a call expression to another constructor
// then it means that all struct/class fields will be
// initialized after this call.
for (size_t i = 0; i < sc->fieldinit_dim; i++)
sc->fieldinit[i] |= CSXthis_ctor;
if (!sc->intypeof && !(sc->callSuper & CSXhalt))
{
if (sc->noctor || sc->callSuper & CSXlabel)
exp->error("constructor calls not allowed in loops or after labels");
if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
exp->error("multiple constructor calls");
if ((sc->callSuper & CSXreturn) && !(sc->callSuper & CSXany_ctor))
exp->error("an earlier return statement skips constructor");
sc->callSuper |= CSXany_ctor | CSXthis_ctor;
}
tthis = ad->type->addMod(sc->func->type->mod);
if (OverloadSet *os = ad->ctor->isOverloadSet())
exp->f = resolveOverloadSet(exp->loc, sc, os, NULL, tthis, exp->arguments);
else
exp->f = resolveFuncCall(exp->loc, sc, ad->ctor, NULL, tthis, exp->arguments, 0);
if (!exp->f || exp->f->errors)
return setError();
exp->checkDeprecated(sc, exp->f);
exp->checkDisabled(sc, exp->f);
exp->checkPurity(sc, exp->f);
exp->checkSafety(sc, exp->f);
exp->checkNogc(sc, exp->f);
//checkAccess(exp->loc, sc, NULL, exp->f); // necessary?
exp->e1 = new DotVarExp(exp->e1->loc, exp->e1, exp->f, false);
exp->e1 = expressionSemantic(exp->e1, sc);
t1 = exp->e1->type;
// BUG: this should really be done by checking the static
// call graph
if (exp->f == sc->func)
{
exp->error("cyclic constructor call");
return setError();
}
}
else if (exp->e1->op == TOKoverloadset)
{
OverloadSet *os = ((OverExp *)exp->e1)->vars;
exp->f = resolveOverloadSet(exp->loc, sc, os, tiargs, tthis, exp->arguments);
if (!exp->f)
return setError();
if (ethis)
exp->e1 = new DotVarExp(exp->loc, ethis, exp->f, false);
else
exp->e1 = new VarExp(exp->loc, exp->f, false);
goto Lagain;
}
else if (!t1)
{
exp->error("function expected before (), not `%s`", exp->e1->toChars());
return setError();
}
else if (t1->ty == Terror)
{
return setError();
}
else if (t1->ty != Tfunction)
{
TypeFunction *tf;
const char *p;
Dsymbol *s;
exp->f = NULL;
if (exp->e1->op == TOKfunction)
{
// function literal that direct called is always inferred.
assert(((FuncExp *)exp->e1)->fd);
exp->f = ((FuncExp *)exp->e1)->fd;
tf = (TypeFunction *)exp->f->type;
p = "function literal";
}
else if (t1->ty == Tdelegate)
{
TypeDelegate *td = (TypeDelegate *)t1;
assert(td->next->ty == Tfunction);
tf = (TypeFunction *)(td->next);
p = "delegate";
}
else if (t1->ty == Tpointer && ((TypePointer *)t1)->next->ty == Tfunction)
{
tf = (TypeFunction *)(((TypePointer *)t1)->next);
p = "function pointer";
}
else if (exp->e1->op == TOKdotvar &&
((DotVarExp *)exp->e1)->var->isOverDeclaration())
{
DotVarExp *dve = (DotVarExp *)exp->e1;
exp->f = resolveFuncCall(exp->loc, sc, dve->var, tiargs, dve->e1->type, exp->arguments, 2);
if (!exp->f)
return setError();
if (exp->f->needThis())
{
dve->var = exp->f;
dve->type = exp->f->type;
dve->hasOverloads = false;
goto Lagain;
}
exp->e1 = new VarExp(dve->loc, exp->f, false);
Expression *e = new CommaExp(exp->loc, dve->e1, exp);
result = expressionSemantic(e, sc);
return;
}
else if (exp->e1->op == TOKvar &&
((VarExp *)exp->e1)->var->isOverDeclaration())
{
s = ((VarExp *)exp->e1)->var;
goto L2;
}
else if (exp->e1->op == TOKtemplate)
{
s = ((TemplateExp *)exp->e1)->td;
L2:
exp->f = resolveFuncCall(exp->loc, sc, s, tiargs, NULL, exp->arguments);
if (!exp->f || exp->f->errors)
return setError();
if (exp->f->needThis())
{
if (hasThis(sc))
{
// Supply an implicit 'this', as in
// this.ident
Expression *ex = new ThisExp(exp->loc);
ex = expressionSemantic(ex, sc);
exp->e1 = new DotVarExp(exp->loc, ex, exp->f, false);
goto Lagain;
}
else if (isNeedThisScope(sc, exp->f))
{
exp->error("need `this` for `%s` of type `%s`", exp->f->toChars(), exp->f->type->toChars());
return setError();
}
}
exp->e1 = new VarExp(exp->e1->loc, exp->f, false);
goto Lagain;
}
else
{
exp->error("function expected before (), not %s of type %s", exp->e1->toChars(), exp->e1->type->toChars());
return setError();
}
const char *failMessage = NULL;
if (!tf->callMatch(NULL, exp->arguments, 0, &failMessage))
{
OutBuffer buf;
buf.writeByte('(');
argExpTypesToCBuffer(&buf, exp->arguments);
buf.writeByte(')');
if (tthis)
tthis->modToBuffer(&buf);
//printf("tf = %s, args = %s\n", tf->deco, (*exp->arguments)[0]->type->deco);
::error(exp->loc, "%s `%s%s` is not callable using argument types `%s`",
p, exp->e1->toChars(), parametersTypeToChars(tf->parameterList),
buf.peekChars());
if (failMessage)
errorSupplemental(exp->loc, failMessage);
return setError();
}
// Purity and safety check should run after testing arguments matching
if (exp->f)
{
exp->checkPurity(sc, exp->f);
exp->checkSafety(sc, exp->f);
exp->checkNogc(sc, exp->f);
if (exp->f->checkNestedReference(sc, exp->loc))
return setError();
}
else if (sc->func && sc->intypeof != 1 && !(sc->flags & SCOPEctfe))
{
bool err = false;
if (!tf->purity && !(sc->flags & SCOPEdebug) && sc->func->setImpure())
{
exp->error("pure %s `%s` cannot call impure %s `%s`",
sc->func->kind(), sc->func->toPrettyChars(), p, exp->e1->toChars());
err = true;
}
if (!tf->isnogc && sc->func->setGC())
{
exp->error("@nogc %s `%s` cannot call non-@nogc %s `%s`",
sc->func->kind(), sc->func->toPrettyChars(), p, exp->e1->toChars());
err = true;
}
if (tf->trust <= TRUSTsystem && sc->func->setUnsafe())
{
exp->error("@safe %s `%s` cannot call @system %s `%s`",
sc->func->kind(), sc->func->toPrettyChars(), p, exp->e1->toChars());
err = true;
}
if (err)
return setError();
}
if (t1->ty == Tpointer)
{
Expression *e = new PtrExp(exp->loc, exp->e1);
e->type = tf;
exp->e1 = e;
}
t1 = tf;
}
else if (exp->e1->op == TOKvar)
{
// Do overload resolution
VarExp *ve = (VarExp *)exp->e1;
exp->f = ve->var->isFuncDeclaration();
assert(exp->f);
tiargs = NULL;
if (exp->f->overnext)
exp->f = resolveFuncCall(exp->loc, sc, exp->f, tiargs, NULL, exp->arguments, 2);
else
{
exp->f = exp->f->toAliasFunc();
TypeFunction *tf = (TypeFunction *)exp->f->type;
const char *failMessage = NULL;
if (!tf->callMatch(NULL, exp->arguments, 0, &failMessage))
{
OutBuffer buf;
buf.writeByte('(');
argExpTypesToCBuffer(&buf, exp->arguments);
buf.writeByte(')');
//printf("tf = %s, args = %s\n", tf->deco, (*exp->arguments)[0]->type->deco);
::error(exp->loc, "%s `%s%s` is not callable using argument types `%s`",
exp->f->kind(), exp->e1->toChars(), parametersTypeToChars(tf->parameterList),
buf.peekChars());
if (failMessage)
errorSupplemental(exp->loc, failMessage);
exp->f = NULL;
}
}
if (!exp->f || exp->f->errors)
return setError();
if (exp->f->needThis())
{
// Change the ancestor lambdas to delegate before hasThis(sc) call.
if (exp->f->checkNestedReference(sc, exp->loc))
return setError();
if (hasThis(sc))
{
// Supply an implicit 'this', as in
// this.ident
Expression *ex = new ThisExp(exp->loc);
ex = expressionSemantic(ex, sc);
exp->e1 = new DotVarExp(exp->loc, ex, ve->var);
// Note: we cannot use f directly, because further overload resolution
// through the supplied 'this' may cause different result.
goto Lagain;
}
else if (isNeedThisScope(sc, exp->f))
{
exp->error("need `this` for `%s` of type `%s`", exp->f->toChars(), exp->f->type->toChars());
return setError();
}
}
exp->checkDeprecated(sc, exp->f);
exp->checkDisabled(sc, exp->f);
exp->checkPurity(sc, exp->f);
exp->checkSafety(sc, exp->f);
exp->checkNogc(sc, exp->f);
checkAccess(exp->loc, sc, NULL, exp->f);
if (exp->f->checkNestedReference(sc, exp->loc))
return setError();
ethis = NULL;
tthis = NULL;
if (ve->hasOverloads)
{
exp->e1 = new VarExp(ve->loc, exp->f, false);
exp->e1->type = exp->f->type;
}
t1 = exp->f->type;
}
assert(t1->ty == Tfunction);
Expression *argprefix;
if (!exp->arguments)
exp->arguments = new Expressions();
if (functionParameters(exp->loc, sc, (TypeFunction *)(t1), tthis, exp->arguments, exp->f, &exp->type, &argprefix))
return setError();
if (!exp->type)
{
exp->e1 = e1org; // Bugzilla 10922, avoid recursive expression printing
exp->error("forward reference to inferred return type of function call `%s`", exp->toChars());
return setError();
}
if (exp->f && exp->f->tintro)
{
Type *t = exp->type;
int offset = 0;
TypeFunction *tf = (TypeFunction *)exp->f->tintro;
if (tf->next->isBaseOf(t, &offset) && offset)
{
exp->type = tf->next;
result = Expression::combine(argprefix, exp->castTo(sc, t));
return;
}
}
// Handle the case of a direct lambda call
if (exp->f && exp->f->isFuncLiteralDeclaration() &&
sc->func && !sc->intypeof)
{
exp->f->tookAddressOf = 0;
}
result = Expression::combine(argprefix, exp);
}
void visit(AddrExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = unaSemantic(exp, sc))
{
result = ex;
return;
}
int wasCond = exp->e1->op == TOKquestion;
if (exp->e1->op == TOKdotti)
{
DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)exp->e1;
TemplateInstance *ti = dti->ti;
{
//assert(ti->needsTypeInference(sc));
dsymbolSemantic(ti, sc);
if (!ti->inst || ti->errors) // if template failed to expand
return setError();
Dsymbol *s = ti->toAlias();
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
exp->e1 = new DotVarExp(exp->e1->loc, dti->e1, f);
exp->e1 = expressionSemantic(exp->e1, sc);
}
}
}
else if (exp->e1->op == TOKscope)
{
TemplateInstance *ti = ((ScopeExp *)exp->e1)->sds->isTemplateInstance();
if (ti)
{
//assert(ti->needsTypeInference(sc));
dsymbolSemantic(ti, sc);
if (!ti->inst || ti->errors) // if template failed to expand
return setError();
Dsymbol *s = ti->toAlias();
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
exp->e1 = new VarExp(exp->e1->loc, f);
exp->e1 = expressionSemantic(exp->e1, sc);
}
}
}
exp->e1 = exp->e1->toLvalue(sc, NULL);
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
if (checkNonAssignmentArrayOp(exp->e1))
return setError();
if (!exp->e1->type)
{
exp->error("cannot take address of %s", exp->e1->toChars());
return setError();
}
bool hasOverloads = false;
if (FuncDeclaration *f = isFuncAddress(exp, &hasOverloads))
{
if (!hasOverloads && f->checkForwardRef(exp->loc))
return setError();
}
else if (!exp->e1->type->deco)
{
if (exp->e1->op == TOKvar)
{
VarExp *ve = (VarExp *)exp->e1;
Declaration *d = ve->var;
exp->error("forward reference to %s %s", d->kind(), d->toChars());
}
else
exp->error("forward reference to %s", exp->e1->toChars());
return setError();
}
exp->type = exp->e1->type->pointerTo();
// See if this should really be a delegate
if (exp->e1->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)exp->e1;
FuncDeclaration *f = dve->var->isFuncDeclaration();
if (f)
{
f = f->toAliasFunc(); // FIXME, should see overloads - Bugzilla 1983
if (!dve->hasOverloads)
f->tookAddressOf++;
Expression *e;
if (f->needThis())
e = new DelegateExp(exp->loc, dve->e1, f, dve->hasOverloads);
else // It is a function pointer. Convert &v.f() --> (v, &V.f())
e = new CommaExp(exp->loc, dve->e1, new AddrExp(exp->loc, new VarExp(exp->loc, f, dve->hasOverloads)));
e = expressionSemantic(e, sc);
result = e;
return;
}
// Look for misaligned pointer in @safe mode
if (checkUnsafeAccess(sc, dve, !exp->type->isMutable(), true))
return setError();
if (dve->e1->op == TOKvar && global.params.vsafe)
{
VarExp *ve = (VarExp *)dve->e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v)
{
if (!checkAddressVar(sc, exp, v))
return setError();
}
}
else if ((dve->e1->op == TOKthis || dve->e1->op == TOKsuper) && global.params.vsafe)
{
ThisExp *ve = (ThisExp *)dve->e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v && v->storage_class & STCref)
{
if (!checkAddressVar(sc, exp, v))
return setError();
}
}
}
else if (exp->e1->op == TOKvar)
{
VarExp *ve = (VarExp *)exp->e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v)
{
if (!checkAddressVar(sc, exp, v))
return setError();
ve->checkPurity(sc, v);
}
FuncDeclaration *f = ve->var->isFuncDeclaration();
if (f)
{
/* Because nested functions cannot be overloaded,
* mark here that we took its address because castTo()
* may not be called with an exact match.
*/
if (!ve->hasOverloads || f->isNested())
f->tookAddressOf++;
if (f->isNested())
{
if (f->isFuncLiteralDeclaration())
{
if (!f->FuncDeclaration::isNested())
{
/* Supply a 'null' for a this pointer if no this is available
*/
Expression *e = new DelegateExp(exp->loc, new NullExp(exp->loc, Type::tnull), f, ve->hasOverloads);
e = expressionSemantic(e, sc);
result = e;
return;
}
}
Expression *e = new DelegateExp(exp->loc, exp->e1, f, ve->hasOverloads);
e = expressionSemantic(e, sc);
result = e;
return;
}
if (f->needThis())
{
if (hasThis(sc))
{
/* Should probably supply 'this' after overload resolution,
* not before.
*/
Expression *ethis = new ThisExp(exp->loc);
Expression *e = new DelegateExp(exp->loc, ethis, f, ve->hasOverloads);
e = expressionSemantic(e, sc);
result = e;
return;
}
if (sc->func && !sc->intypeof)
{
if (sc->func->setUnsafe())
{
exp->error("`this` reference necessary to take address of member %s in @safe function %s",
f->toChars(), sc->func->toChars());
}
}
}
}
}
else if ((exp->e1->op == TOKthis || exp->e1->op == TOKsuper) && global.params.vsafe)
{
ThisExp *ve = (ThisExp *)exp->e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v)
{
if (!checkAddressVar(sc, exp, v))
return setError();
}
}
else if (exp->e1->op == TOKcall)
{
CallExp *ce = (CallExp *)exp->e1;
if (ce->e1->type->ty == Tfunction)
{
TypeFunction *tf = (TypeFunction *)ce->e1->type;
if (tf->isref && sc->func && !sc->intypeof && sc->func->setUnsafe())
{
exp->error("cannot take address of ref return of %s() in @safe function %s",
ce->e1->toChars(), sc->func->toChars());
}
}
}
else if (exp->e1->op == TOKindex)
{
/* For:
* int[3] a;
* &a[i]
* check 'a' the same as for a regular variable
*/
IndexExp *ei = (IndexExp *)exp->e1;
Type *tyi = ei->e1->type->toBasetype();
if (tyi->ty == Tsarray && ei->e1->op == TOKvar)
{
VarExp *ve = (VarExp *)ei->e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v)
{
if (!checkAddressVar(sc, exp, v))
return setError();
ve->checkPurity(sc, v);
}
}
}
else if (wasCond)
{
/* a ? b : c was transformed to *(a ? &b : &c), but we still
* need to do safety checks
*/
assert(exp->e1->op == TOKstar);
PtrExp *pe = (PtrExp *)exp->e1;
assert(pe->e1->op == TOKquestion);
CondExp *ce = (CondExp *)pe->e1;
assert(ce->e1->op == TOKaddress);
assert(ce->e2->op == TOKaddress);
// Re-run semantic on the address expressions only
ce->e1->type = NULL;
ce->e1 = expressionSemantic(ce->e1, sc);
ce->e2->type = NULL;
ce->e2 = expressionSemantic(ce->e2, sc);
}
result = exp->optimize(WANTvalue);
}
void visit(PtrExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
Type *tb = exp->e1->type->toBasetype();
switch (tb->ty)
{
case Tpointer:
exp->type = ((TypePointer *)tb)->next;
break;
case Tsarray:
case Tarray:
exp->error("using * on an array is no longer supported; use *(%s).ptr instead", exp->e1->toChars());
exp->type = ((TypeArray *)tb)->next;
exp->e1 = exp->e1->castTo(sc, exp->type->pointerTo());
break;
case Tnull:
exp->type = Type::tnoreturn; // typeof(*null) is bottom type
break;
default:
exp->error("can only * a pointer, not a `%s`", exp->e1->type->toChars());
/* fall through */
case Terror:
return setError();
}
if (exp->checkValue())
return setError();
result = exp;
}
void visit(NegExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
exp->type = exp->e1->type;
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp->e1))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (!target.isVectorOpSupported(tb, exp->op))
{
result = exp->incompatibleTypes();
return;
}
if (exp->e1->checkNoBool())
return setError();
if (exp->e1->checkArithmetic())
return setError();
result = exp;
}
void visit(UAddExp *exp)
{
assert(!exp->type);
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op))
{
result = exp->incompatibleTypes();
return;
}
if (exp->e1->checkNoBool())
return setError();
if (exp->e1->checkArithmetic())
return setError();
result = exp->e1;
}
void visit(ComExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
exp->type = exp->e1->type;
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp->e1))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (!target.isVectorOpSupported(tb, exp->op))
{
result = exp->incompatibleTypes();
return;
}
if (exp->e1->checkNoBool())
return setError();
if (exp->e1->checkIntegral())
return setError();
result = exp;
}
void visit(NotExp *e)
{
if (e->type)
{
result = e;
return;
}
setNoderefOperand(e);
// Note there is no operator overload
if (Expression *ex = unaSemantic(e, sc))
{
result = ex;
return;
}
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
if (e->e1->op == TOKtype)
e->e1 = resolveAliasThis(sc, e->e1);
e->e1 = resolveProperties(sc, e->e1);
e->e1 = e->e1->toBoolean(sc);
if (e->e1->type == Type::terror)
{
result = e->e1;
return;
}
if (!target.isVectorOpSupported(e->e1->type->toBasetype(), e->op))
{
result = e->incompatibleTypes();
return;
}
// Bugzilla 13910: Today NotExp can take an array as its operand.
if (checkNonAssignmentArrayOp(e->e1))
return setError();
e->type = Type::tbool;
result = e;
}
void visit(DeleteExp *exp)
{
if (Expression *ex = unaSemantic(exp, sc))
{
result = ex;
return;
}
exp->e1 = resolveProperties(sc, exp->e1);
exp->e1 = exp->e1->modifiableLvalue(sc, NULL);
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
exp->type = Type::tvoid;
AggregateDeclaration *ad = NULL;
Type *tb = exp->e1->type->toBasetype();
switch (tb->ty)
{ case Tclass:
{
ClassDeclaration *cd = ((TypeClass *)tb)->sym;
if (cd->isCOMinterface())
{ /* Because COM classes are deleted by IUnknown.Release()
*/
exp->error("cannot delete instance of COM interface %s", cd->toChars());
return setError();
}
ad = cd;
break;
}
case Tpointer:
tb = ((TypePointer *)tb)->next->toBasetype();
if (tb->ty == Tstruct)
{
ad = ((TypeStruct *)tb)->sym;
FuncDeclaration *f = ad->aggDelete;
FuncDeclaration *fd = ad->dtor;
if (!f)
{
semanticTypeInfo(sc, tb);
break;
}
/* Construct:
* ea = copy e1 to a tmp to do side effects only once
* eb = call destructor
* ec = call deallocator
*/
Expression *ea = NULL;
Expression *eb = NULL;
Expression *ec = NULL;
VarDeclaration *v = NULL;
if (fd && f)
{
v = copyToTemp(0, "__tmpea", exp->e1);
dsymbolSemantic(v, sc);
ea = new DeclarationExp(exp->loc, v);
ea->type = v->type;
}
if (fd)
{
Expression *e = ea ? new VarExp(exp->loc, v) : exp->e1;
e = new DotVarExp(Loc(), e, fd, false);
eb = new CallExp(exp->loc, e);
eb = expressionSemantic(eb, sc);
}
if (f)
{
Type *tpv = Type::tvoid->pointerTo();
Expression *e = ea ? new VarExp(exp->loc, v) : exp->e1->castTo(sc, tpv);
e = new CallExp(exp->loc, new VarExp(exp->loc, f, false), e);
ec = expressionSemantic(e, sc);
}
ea = Expression::combine(ea, eb);
ea = Expression::combine(ea, ec);
assert(ea);
result = ea;
return;
}
break;
case Tarray:
{
Type *tv = tb->nextOf()->baseElemOf();
if (tv->ty == Tstruct)
{
ad = ((TypeStruct *)tv)->sym;
if (ad->dtor)
semanticTypeInfo(sc, ad->type);
}
break;
}
default:
exp->error("cannot delete type %s", exp->e1->type->toChars());
return setError();
}
bool err = false;
if (ad)
{
if (ad->dtor)
{
err |= exp->checkPurity(sc, ad->dtor);
err |= exp->checkSafety(sc, ad->dtor);
err |= exp->checkNogc(sc, ad->dtor);
}
if (ad->aggDelete && tb->ty != Tarray)
{
err |= exp->checkPurity(sc, ad->aggDelete);
err |= exp->checkSafety(sc, ad->aggDelete);
err |= exp->checkNogc(sc, ad->aggDelete);
}
if (err)
return setError();
}
if (!sc->intypeof && sc->func &&
!exp->isRAII &&
sc->func->setUnsafe())
{
exp->error("%s is not @safe but is used in @safe function %s", exp->toChars(), sc->func->toChars());
err = true;
}
if (err)
return setError();
result = exp;
}
void visit(CastExp *exp)
{
//static int x; assert(++x < 10);
if (exp->type)
{
result = exp;
return;
}
if (exp->to)
{
exp->to = typeSemantic(exp->to, exp->loc, sc);
if (exp->to == Type::terror)
return setError();
if (!exp->to->hasPointers())
setNoderefOperand(exp);
// When e1 is a template lambda, this cast may instantiate it with
// the type 'to'.
exp->e1 = inferType(exp->e1, exp->to);
}
if (Expression *ex = unaSemantic(exp, sc))
{
result = ex;
return;
}
Expression *e1x = resolveProperties(sc, exp->e1);
if (e1x->op == TOKerror)
{
result = e1x;
return;
}
if (e1x->checkType())
return setError();
exp->e1 = e1x;
if (!exp->e1->type)
{
exp->error("cannot cast %s", exp->e1->toChars());
return setError();
}
if (!exp->to) // Handle cast(const) and cast(immutable), etc.
{
exp->to = exp->e1->type->castMod(exp->mod);
exp->to = typeSemantic(exp->to, exp->loc, sc);
if (exp->to == Type::terror)
return setError();
}
if (exp->to->ty == Ttuple)
{
exp->error("cannot cast %s to tuple type %s", exp->e1->toChars(), exp->to->toChars());
return setError();
}
if (exp->e1->type->ty != Tvoid ||
(exp->e1->op == TOKfunction && exp->to->ty == Tvoid) ||
exp->e1->op == TOKtype ||
exp->e1->op == TOKtemplate)
{
if (exp->e1->checkValue())
return setError();
}
// cast(void) is used to mark e1 as unused, so it is safe
if (exp->to->ty == Tvoid)
{
exp->type = exp->to;
result = exp;
return;
}
if (!exp->to->equals(exp->e1->type) && exp->mod == (unsigned char)~0)
{
if (Expression *e = exp->op_overload(sc))
{
result = e->implicitCastTo(sc, exp->to);
return;
}
}
Type *t1b = exp->e1->type->toBasetype();
Type *tob = exp->to->toBasetype();
if (tob->ty == Tstruct && !tob->equals(t1b))
{
/* Look to replace:
* cast(S)t
* with:
* S(t)
*/
// Rewrite as to.call(e1)
Expression *e = new TypeExp(exp->loc, exp->to);
e = new CallExp(exp->loc, e, exp->e1);
e = trySemantic(e, sc);
if (e)
{
result = e;
return;
}
}
if (!t1b->equals(tob) && (t1b->ty == Tarray || t1b->ty == Tsarray))
{
if (checkNonAssignmentArrayOp(exp->e1))
return setError();
}
// Look for casting to a vector type
if (tob->ty == Tvector && t1b->ty != Tvector)
{
result = new VectorExp(exp->loc, exp->e1, exp->to);
return;
}
Expression *ex = exp->e1->castTo(sc, exp->to);
if (ex->op == TOKerror)
{
result = ex;
return;
}
// Check for unsafe casts
if (sc->func && !sc->intypeof &&
!isSafeCast(ex, t1b, tob) &&
sc->func->setUnsafe())
{
exp->error("cast from %s to %s not allowed in safe code", exp->e1->type->toChars(), exp->to->toChars());
return setError();
}
result = ex;
}
void visit(VectorExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
exp->e1 = expressionSemantic(exp->e1, sc);
exp->type = typeSemantic(exp->to, exp->loc, sc);
if (exp->e1->op == TOKerror || exp->type->ty == Terror)
{
result = exp->e1;
return;
}
Type *tb = exp->type->toBasetype();
assert(tb->ty == Tvector);
TypeVector *tv = (TypeVector *)tb;
Type *te = tv->elementType();
exp->dim = (int)(tv->size(exp->loc) / te->size(exp->loc));
exp->e1 = exp->e1->optimize(WANTvalue);
bool res = false;
if (exp->e1->op == TOKarrayliteral)
{
for (size_t i = 0; i < exp->dim; i++)
{
// Do not stop on first error - check all AST nodes even if error found
res |= checkVectorElem(exp, ((ArrayLiteralExp *)exp->e1)->getElement(i));
}
}
else if (exp->e1->type->ty == Tvoid)
res = checkVectorElem(exp, exp->e1);
Expression *e = exp;
if (res)
e = new ErrorExp();
result = e;
}
void visit(VectorArrayExp *e)
{
if (!e->type)
{
unaSemantic(e, sc);
e->e1 = resolveProperties(sc, e->e1);
if (e->e1->op == TOKerror)
{
result = e->e1;
return;
}
assert(e->e1->type->ty == Tvector);
TypeVector *tv = (TypeVector *)e->e1->type;
e->type = tv->basetype;
}
result = e;
}
void visit(SliceExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
// operator overloading should be handled in ArrayExp already.
if (Expression *ex = unaSemantic(exp, sc))
{
result = ex;
return;
}
exp->e1 = resolveProperties(sc, exp->e1);
if (exp->e1->op == TOKtype && exp->e1->type->ty != Ttuple)
{
if (exp->lwr || exp->upr)
{
exp->error("cannot slice type `%s`", exp->e1->toChars());
return setError();
}
Expression *e = new TypeExp(exp->loc, exp->e1->type->arrayOf());
result = expressionSemantic(e, sc);
return;
}
if (!exp->lwr && !exp->upr)
{
if (exp->e1->op == TOKarrayliteral)
{
// Convert [a,b,c][] to [a,b,c]
Type *t1b = exp->e1->type->toBasetype();
Expression *e = exp->e1;
if (t1b->ty == Tsarray)
{
e = e->copy();
e->type = t1b->nextOf()->arrayOf();
}
result = e;
return;
}
if (exp->e1->op == TOKslice)
{
// Convert e[][] to e[]
SliceExp *se = (SliceExp *)exp->e1;
if (!se->lwr && !se->upr)
{
result = se;
return;
}
}
if (isArrayOpOperand(exp->e1))
{
// Convert (a[]+b[])[] to a[]+b[]
result = exp->e1;
return;
}
}
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
if (exp->e1->type->ty == Terror)
return setError();
Type *t1b = exp->e1->type->toBasetype();
if (t1b->ty == Tpointer)
{
if (((TypePointer *)t1b)->next->ty == Tfunction)
{
exp->error("cannot slice function pointer %s", exp->e1->toChars());
return setError();
}
if (!exp->lwr || !exp->upr)
{
exp->error("need upper and lower bound to slice pointer");
return setError();
}
if (sc->func && !sc->intypeof && sc->func->setUnsafe())
{
exp->error("pointer slicing not allowed in safe functions");
return setError();
}
}
else if (t1b->ty == Tarray)
{
}
else if (t1b->ty == Tsarray)
{
if (!exp->arrayop && global.params.vsafe)
{
/* Slicing a static array is like taking the address of it.
* Perform checks as if e[] was &e
*/
VarDeclaration *v = NULL;
if (exp->e1->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)exp->e1;
if (dve->e1->op == TOKvar)
{
VarExp *ve = (VarExp *)dve->e1;
v = ve->var->isVarDeclaration();
}
else if (dve->e1->op == TOKthis || dve->e1->op == TOKsuper)
{
ThisExp *ve = (ThisExp *)dve->e1;
v = ve->var->isVarDeclaration();
if (v && !(v->storage_class & STCref))
v = NULL;
}
}
else if (exp->e1->op == TOKvar)
{
VarExp *ve = (VarExp *)exp->e1;
v = ve->var->isVarDeclaration();
}
else if (exp->e1->op == TOKthis || exp->e1->op == TOKsuper)
{
ThisExp *ve = (ThisExp *)exp->e1;
v = ve->var->isVarDeclaration();
}
if (v)
{
if (!checkAddressVar(sc, exp, v))
return setError();
}
}
}
else if (t1b->ty == Ttuple)
{
if (!exp->lwr && !exp->upr)
{
result = exp->e1;
return;
}
if (!exp->lwr || !exp->upr)
{
exp->error("need upper and lower bound to slice tuple");
return setError();
}
}
else if (t1b->ty == Tvector)
{
// Convert e1 to corresponding static array
TypeVector *tv1 = (TypeVector *)t1b;
t1b = tv1->basetype;
t1b = t1b->castMod(tv1->mod);
exp->e1->type = t1b;
}
else
{
exp->error("%s cannot be sliced with []",
t1b->ty == Tvoid ? exp->e1->toChars() : t1b->toChars());
return setError();
}
/* Run semantic on lwr and upr.
*/
Scope *scx = sc;
if (t1b->ty == Tsarray || t1b->ty == Tarray || t1b->ty == Ttuple)
{
// Create scope for 'length' variable
ScopeDsymbol *sym = new ArrayScopeSymbol(sc, exp);
sym->loc = exp->loc;
sym->parent = sc->scopesym;
sc = sc->push(sym);
}
if (exp->lwr)
{
if (t1b->ty == Ttuple) sc = sc->startCTFE();
exp->lwr = expressionSemantic(exp->lwr, sc);
exp->lwr = resolveProperties(sc, exp->lwr);
if (t1b->ty == Ttuple) sc = sc->endCTFE();
exp->lwr = exp->lwr->implicitCastTo(sc, Type::tsize_t);
}
if (exp->upr)
{
if (t1b->ty == Ttuple) sc = sc->startCTFE();
exp->upr = expressionSemantic(exp->upr, sc);
exp->upr = resolveProperties(sc, exp->upr);
if (t1b->ty == Ttuple) sc = sc->endCTFE();
exp->upr = exp->upr->implicitCastTo(sc, Type::tsize_t);
}
if (sc != scx)
sc = sc->pop();
if ((exp->lwr && exp->lwr->type == Type::terror) ||
(exp->upr && exp->upr->type == Type::terror))
{
return setError();
}
if (t1b->ty == Ttuple)
{
exp->lwr = exp->lwr->ctfeInterpret();
exp->upr = exp->upr->ctfeInterpret();
uinteger_t i1 = exp->lwr->toUInteger();
uinteger_t i2 = exp->upr->toUInteger();
TupleExp *te;
TypeTuple *tup;
size_t length;
if (exp->e1->op == TOKtuple) // slicing an expression tuple
{
te = (TupleExp *)exp->e1;
tup = NULL;
length = te->exps->length;
}
else if (exp->e1->op == TOKtype) // slicing a type tuple
{
te = NULL;
tup = (TypeTuple *)t1b;
length = Parameter::dim(tup->arguments);
}
else
assert(0);
if (i2 < i1 || length < i2)
{
exp->error("string slice [%llu .. %llu] is out of bounds", i1, i2);
return setError();
}
size_t j1 = (size_t) i1;
size_t j2 = (size_t) i2;
Expression *e;
if (exp->e1->op == TOKtuple)
{
Expressions *exps = new Expressions;
exps->setDim(j2 - j1);
for (size_t i = 0; i < j2 - j1; i++)
{
(*exps)[i] = (*te->exps)[j1 + i];
}
e = new TupleExp(exp->loc, te->e0, exps);
}
else
{
Parameters *args = new Parameters;
args->reserve(j2 - j1);
for (size_t i = j1; i < j2; i++)
{
Parameter *arg = Parameter::getNth(tup->arguments, i);
args->push(arg);
}
e = new TypeExp(exp->e1->loc, new TypeTuple(args));
}
e = expressionSemantic(e, sc);
result = e;
return;
}
exp->type = t1b->nextOf()->arrayOf();
// Allow typedef[] -> typedef[]
if (exp->type->equals(t1b))
exp->type = exp->e1->type;
if (exp->lwr && exp->upr)
{
exp->lwr = exp->lwr->optimize(WANTvalue);
exp->upr = exp->upr->optimize(WANTvalue);
IntRange lwrRange = getIntRange(exp->lwr);
IntRange uprRange = getIntRange(exp->upr);
if (t1b->ty == Tsarray || t1b->ty == Tarray)
{
Expression *el = new ArrayLengthExp(exp->loc, exp->e1);
el = expressionSemantic(el, sc);
el = el->optimize(WANTvalue);
if (el->op == TOKint64)
{
dinteger_t length = el->toInteger();
IntRange bounds(SignExtendedNumber(0), SignExtendedNumber(length));
exp->upperIsInBounds = bounds.contains(uprRange);
}
}
else if (t1b->ty == Tpointer)
{
exp->upperIsInBounds = true;
}
else
assert(0);
exp->lowerIsLessThanUpper = (lwrRange.imax <= uprRange.imin);
//printf("upperIsInBounds = %d lowerIsLessThanUpper = %d\n", upperIsInBounds, lowerIsLessThanUpper);
}
result = exp;
}
void visit(ArrayLengthExp *e)
{
if (e->type)
{
result = e;
return;
}
if (Expression *ex = unaSemantic(e, sc))
{
result = ex;
return;
}
e->e1 = resolveProperties(sc, e->e1);
e->type = Type::tsize_t;
result = e;
}
void visit(IntervalExp *e)
{
if (e->type)
{
result = e;
return;
}
Expression *le = e->lwr;
le = expressionSemantic(le, sc);
le = resolveProperties(sc, le);
Expression *ue = e->upr;
ue = expressionSemantic(ue, sc);
ue = resolveProperties(sc, ue);
if (le->op == TOKerror)
{
result = le;
return;
}
if (ue->op == TOKerror)
{
result = ue;
return;
}
e->lwr = le;
e->upr = ue;
e->type = Type::tvoid;
result = e;
}
void visit(DelegatePtrExp *e)
{
if (!e->type)
{
unaSemantic(e, sc);
e->e1 = resolveProperties(sc, e->e1);
if (e->e1->op == TOKerror)
{
result = e->e1;
return;
}
e->type = Type::tvoidptr;
}
result = e;
}
void visit(DelegateFuncptrExp *e)
{
if (!e->type)
{
unaSemantic(e, sc);
e->e1 = resolveProperties(sc, e->e1);
if (e->e1->op == TOKerror)
{
result = e->e1;
return;
}
e->type = e->e1->type->nextOf()->pointerTo();
}
result = e;
}
void visit(ArrayExp *exp)
{
assert(!exp->type);
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (isAggregate(exp->e1->type))
exp->error("no [] operator overload for type %s", exp->e1->type->toChars());
else
exp->error("only one index allowed to index %s", exp->e1->type->toChars());
return setError();
}
void visit(DotExp *exp)
{
exp->e1 = expressionSemantic(exp->e1, sc);
exp->e2 = expressionSemantic(exp->e2, sc);
if (exp->e1->op == TOKtype)
{
result = exp->e2;
return;
}
if (exp->e2->op == TOKtype)
{
result = exp->e2;
return;
}
if (exp->e2->op == TOKtemplate)
{
TemplateDeclaration *td = ((TemplateExp *)exp->e2)->td;
Expression *e = new DotTemplateExp(exp->loc, exp->e1, td);
result = expressionSemantic(e, sc);
return;
}
if (!exp->type)
exp->type = exp->e2->type;
result = exp;
}
void visit(CommaExp *e)
{
if (e->type)
{
result = e;
return;
}
// Allow `((a,b),(x,y))`
if (e->allowCommaExp)
{
if (e->e1 && e->e1->op == TOKcomma)
((CommaExp *)e->e1)->allowCommaExp = true;
if (e->e2 && e->e2->op == TOKcomma)
((CommaExp *)e->e2)->allowCommaExp = true;
}
if (Expression *ex = binSemanticProp(e, sc))
{
result = ex;
return;
}
e->e1 = e->e1->addDtorHook(sc);
if (checkNonAssignmentArrayOp(e->e1))
return setError();
e->type = e->e2->type;
if (e->type != Type::tvoid && !e->allowCommaExp && !e->isGenerated)
e->deprecation("Using the result of a comma expression is deprecated");
result = e;
}
void visit(IndexExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
// operator overloading should be handled in ArrayExp already.
if (!exp->e1->type)
exp->e1 = expressionSemantic(exp->e1, sc);
assert(exp->e1->type); // semantic() should already be run on it
if (exp->e1->op == TOKtype && exp->e1->type->ty != Ttuple)
{
exp->e2 = expressionSemantic(exp->e2, sc);
exp->e2 = resolveProperties(sc, exp->e2);
Type *nt;
if (exp->e2->op == TOKtype)
nt = new TypeAArray(exp->e1->type, exp->e2->type);
else
nt = new TypeSArray(exp->e1->type, exp->e2);
Expression *e = new TypeExp(exp->loc, nt);
result = expressionSemantic(e, sc);
return;
}
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
if (exp->e1->type->ty == Terror)
return setError();
// Note that unlike C we do not implement the int[ptr]
Type *t1b = exp->e1->type->toBasetype();
if (t1b->ty == Tvector)
{
// Convert e1 to corresponding static array
TypeVector *tv1 = (TypeVector *)t1b;
t1b = tv1->basetype;
t1b = t1b->castMod(tv1->mod);
exp->e1->type = t1b;
}
/* Run semantic on e2
*/
Scope *scx = sc;
if (t1b->ty == Tsarray || t1b->ty == Tarray || t1b->ty == Ttuple)
{
// Create scope for 'length' variable
ScopeDsymbol *sym = new ArrayScopeSymbol(sc, exp);
sym->loc = exp->loc;
sym->parent = sc->scopesym;
sc = sc->push(sym);
}
if (t1b->ty == Ttuple) sc = sc->startCTFE();
exp->e2 = expressionSemantic(exp->e2, sc);
exp->e2 = resolveProperties(sc, exp->e2);
if (t1b->ty == Ttuple) sc = sc->endCTFE();
if (exp->e2->op == TOKtuple)
{
TupleExp *te = (TupleExp *)exp->e2;
if (te->exps && te->exps->length == 1)
exp->e2 = Expression::combine(te->e0, (*te->exps)[0]); // bug 4444 fix
}
if (sc != scx)
sc = sc->pop();
if (exp->e2->type == Type::terror)
return setError();
if (checkNonAssignmentArrayOp(exp->e1))
return setError();
switch (t1b->ty)
{
case Tpointer:
if (((TypePointer *)t1b)->next->ty == Tfunction)
{
exp->error("cannot index function pointer %s", exp->e1->toChars());
return setError();
}
exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
if (exp->e2->type == Type::terror)
return setError();
exp->e2 = exp->e2->optimize(WANTvalue);
if (exp->e2->op == TOKint64 && exp->e2->toInteger() == 0)
;
else if (sc->func && sc->func->setUnsafe())
{
exp->error("safe function `%s` cannot index pointer `%s`",
sc->func->toPrettyChars(), exp->e1->toChars());
return setError();
}
exp->type = ((TypeNext *)t1b)->next;
break;
case Tarray:
exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
if (exp->e2->type == Type::terror)
return setError();
exp->type = ((TypeNext *)t1b)->next;
break;
case Tsarray:
{
exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
if (exp->e2->type == Type::terror)
return setError();
exp->type = t1b->nextOf();
break;
}
case Taarray:
{
TypeAArray *taa = (TypeAArray *)t1b;
/* We can skip the implicit conversion if they differ only by
* constness (Bugzilla 2684, see also bug 2954b)
*/
if (!arrayTypeCompatibleWithoutCasting(exp->e2->type, taa->index))
{
exp->e2 = exp->e2->implicitCastTo(sc, taa->index); // type checking
if (exp->e2->type == Type::terror)
return setError();
}
semanticTypeInfo(sc, taa);
exp->type = taa->next;
break;
}
case Ttuple:
{
exp->e2 = exp->e2->implicitCastTo(sc, Type::tsize_t);
if (exp->e2->type == Type::terror)
return setError();
exp->e2 = exp->e2->ctfeInterpret();
uinteger_t index = exp->e2->toUInteger();
TupleExp *te;
TypeTuple *tup;
size_t length;
if (exp->e1->op == TOKtuple)
{
te = (TupleExp *)exp->e1;
tup = NULL;
length = te->exps->length;
}
else if (exp->e1->op == TOKtype)
{
te = NULL;
tup = (TypeTuple *)t1b;
length = Parameter::dim(tup->arguments);
}
else
assert(0);
if (length <= index)
{
exp->error("array index [%llu] is outside array bounds [0 .. %llu]",
index, (ulonglong)length);
return setError();
}
Expression *e;
if (exp->e1->op == TOKtuple)
{
e = (*te->exps)[(size_t)index];
e = Expression::combine(te->e0, e);
}
else
e = new TypeExp(exp->e1->loc, Parameter::getNth(tup->arguments, (size_t)index)->type);
result = e;
return;
}
default:
exp->error("%s must be an array or pointer type, not %s",
exp->e1->toChars(), exp->e1->type->toChars());
return setError();
}
if (t1b->ty == Tsarray || t1b->ty == Tarray)
{
Expression *el = new ArrayLengthExp(exp->loc, exp->e1);
el = expressionSemantic(el, sc);
el = el->optimize(WANTvalue);
if (el->op == TOKint64)
{
exp->e2 = exp->e2->optimize(WANTvalue);
dinteger_t length = el->toInteger();
if (length)
{
IntRange bounds(SignExtendedNumber(0), SignExtendedNumber(length - 1));
exp->indexIsInBounds = bounds.contains(getIntRange(exp->e2));
}
}
}
result = exp;
}
void visit(PostExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemantic(exp, sc))
{
result = ex;
return;
}
Expression *e1x = resolveProperties(sc, exp->e1);
if (e1x->op == TOKerror)
{
result = e1x;
return;
}
exp->e1 = e1x;
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->e1->checkReadModifyWrite(exp->op))
return setError();
if (exp->e1->op == TOKslice)
{
const char *s = exp->op == TOKplusplus ? "increment" : "decrement";
exp->error("cannot post-%s array slice `%s`, use pre-%s instead", s, exp->e1->toChars(), s);
return setError();
}
exp->e1 = exp->e1->optimize(WANTvalue);
Type *t1 = exp->e1->type->toBasetype();
if (t1->ty == Tclass || t1->ty == Tstruct || exp->e1->op == TOKarraylength)
{
/* Check for operator overloading,
* but rewrite in terms of ++e instead of e++
*/
/* If e1 is not trivial, take a reference to it
*/
Expression *de = NULL;
if (exp->e1->op != TOKvar && exp->e1->op != TOKarraylength)
{
// ref v = e1;
VarDeclaration *v = copyToTemp(STCref, "__postref", exp->e1);
de = new DeclarationExp(exp->loc, v);
exp->e1 = new VarExp(exp->e1->loc, v);
}
/* Rewrite as:
* auto tmp = e1; ++e1; tmp
*/
VarDeclaration *tmp = copyToTemp(0, "__pitmp", exp->e1);
Expression *ea = new DeclarationExp(exp->loc, tmp);
Expression *eb = exp->e1->syntaxCopy();
eb = new PreExp(exp->op == TOKplusplus ? TOKpreplusplus : TOKpreminusminus, exp->loc, eb);
Expression *ec = new VarExp(exp->loc, tmp);
// Combine de,ea,eb,ec
if (de)
ea = new CommaExp(exp->loc, de, ea);
e = new CommaExp(exp->loc, ea, eb);
e = new CommaExp(exp->loc, e, ec);
e = expressionSemantic(e, sc);
result = e;
return;
}
exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
e = exp;
if (exp->e1->checkScalar())
return setError();
if (exp->e1->checkNoBool())
return setError();
if (exp->e1->type->ty == Tpointer)
e = scaleFactor(exp, sc);
else
exp->e2 = exp->e2->castTo(sc, exp->e1->type);
e->type = exp->e1->type;
result = e;
}
void visit(PreExp *exp)
{
Expression *e = exp->op_overload(sc);
// printf("PreExp::semantic('%s')\n", exp->toChars());
if (e)
{
result = e;
return;
}
// Rewrite as e1+=1 or e1-=1
if (exp->op == TOKpreplusplus)
e = new AddAssignExp(exp->loc, exp->e1, new IntegerExp(exp->loc, 1, Type::tint32));
else
e = new MinAssignExp(exp->loc, exp->e1, new IntegerExp(exp->loc, 1, Type::tint32));
result = expressionSemantic(e, sc);
}
void visit(AssignExp *exp)
{
//printf("e1->op = %d, '%s'\n", exp->e1->op, Token::toChars(exp->e1->op));
//printf("e2->op = %d, '%s'\n", exp->e2->op, Token::toChars(exp->e2->op));
if (exp->type)
{
result = exp;
return;
}
Expression *e1old = exp->e1;
if (exp->e2->op == TOKcomma)
{
/* Rewrite to get rid of the comma from rvalue
*/
if (!((CommaExp *)exp->e2)->isGenerated)
exp->deprecation("Using the result of a comma expression is deprecated");
Expression *e0;
exp->e2 = Expression::extractLast(exp->e2, &e0);
Expression *e = Expression::combine(e0, exp);
result = expressionSemantic(e, sc);
return;
}
/* Look for operator overloading of a[arguments] = e2.
* Do it before e1->semantic() otherwise the ArrayExp will have been
* converted to unary operator overloading already.
*/
if (exp->e1->op == TOKarray)
{
Expression *res;
ArrayExp *ae = (ArrayExp *)exp->e1;
ae->e1 = expressionSemantic(ae->e1, sc);
ae->e1 = resolveProperties(sc, ae->e1);
Expression *ae1old = ae->e1;
const bool maybeSlice =
(ae->arguments->length == 0 ||
(ae->arguments->length == 1 && (*ae->arguments)[0]->op == TOKinterval));
IntervalExp *ie = NULL;
if (maybeSlice && ae->arguments->length)
{
assert((*ae->arguments)[0]->op == TOKinterval);
ie = (IntervalExp *)(*ae->arguments)[0];
}
while (true)
{
if (ae->e1->op == TOKerror)
{
result = ae->e1;
return;
}
Expression *e0 = NULL;
Expression *ae1save = ae->e1;
ae->lengthVar = NULL;
Type *t1b = ae->e1->type->toBasetype();
AggregateDeclaration *ad = isAggregate(t1b);
if (!ad)
break;
if (search_function(ad, Id::indexass))
{
// Deal with $
res = resolveOpDollar(sc, ae, &e0);
if (!res) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j)
goto Lfallback;
if (res->op == TOKerror)
{
result = res;
return;
}
res = expressionSemantic(exp->e2, sc);
if (res->op == TOKerror)
{
result = res;
return;
}
exp->e2 = res;
/* Rewrite (a[arguments] = e2) as:
* a.opIndexAssign(e2, arguments)
*/
Expressions *a = (Expressions *)ae->arguments->copy();
a->insert(0, exp->e2);
res = new DotIdExp(exp->loc, ae->e1, Id::indexass);
res = new CallExp(exp->loc, res, a);
if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2)
res = trySemantic(res, sc);
else
res = expressionSemantic(res, sc);
if (res)
{
res = Expression::combine(e0, res);
result = res;
return;
}
}
Lfallback:
if (maybeSlice && search_function(ad, Id::sliceass))
{
// Deal with $
res = resolveOpDollar(sc, ae, ie, &e0);
if (res->op == TOKerror)
{
result = res;
return;
}
res = expressionSemantic(exp->e2, sc);
if (res->op == TOKerror)
{
result = res;
return;
}
exp->e2 = res;
/* Rewrite (a[i..j] = e2) as:
* a.opSliceAssign(e2, i, j)
*/
Expressions *a = new Expressions();
a->push(exp->e2);
if (ie)
{
a->push(ie->lwr);
a->push(ie->upr);
}
res = new DotIdExp(exp->loc, ae->e1, Id::sliceass);
res = new CallExp(exp->loc, res, a);
res = expressionSemantic(res, sc);
res = Expression::combine(e0, res);
result = res;
return;
}
// No operator overloading member function found yet, but
// there might be an alias this to try.
if (ad->aliasthis && t1b != ae->att1)
{
if (!ae->att1 && t1b->checkAliasThisRec())
ae->att1 = t1b;
/* Rewrite (a[arguments] op e2) as:
* a.aliasthis[arguments] op e2
*/
ae->e1 = resolveAliasThis(sc, ae1save, true);
if (ae->e1)
continue;
}
break;
}
ae->e1 = ae1old; // recovery
ae->lengthVar = NULL;
}
/* Run exp->e1 semantic.
*/
{
Expression *e1x = exp->e1;
/* With UFCS, e.f = value
* Could mean:
* .f(e, value)
* or:
* .f(e) = value
*/
if (e1x->op == TOKdotti)
{
DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1x;
Expression *e = semanticY(dti, sc, 1);
if (!e)
{
result = resolveUFCSProperties(sc, e1x, exp->e2);
return;
}
e1x = e;
}
else if (e1x->op == TOKdotid)
{
DotIdExp *die = (DotIdExp *)e1x;
Expression *e = semanticY(die, sc, 1);
if (e && isDotOpDispatch(e))
{
unsigned errors = global.startGagging();
e = resolvePropertiesX(sc, e, exp->e2);
if (global.endGagging(errors))
e = NULL; /* fall down to UFCS */
else
{
result = e;
return;
}
}
if (!e)
{
result = resolveUFCSProperties(sc, e1x, exp->e2);
return;
}
e1x = e;
}
else
{
if (e1x->op == TOKslice)
((SliceExp *)e1x)->arrayop = true;
e1x = expressionSemantic(e1x, sc);
}
/* We have f = value.
* Could mean:
* f(value)
* or:
* f() = value
*/
if (Expression *e = resolvePropertiesX(sc, e1x, exp->e2))
{
result = e;
return;
}
if (e1x->checkRightThis(sc))
return setError();
exp->e1 = e1x;
assert(exp->e1->type);
}
Type *t1 = exp->e1->type->toBasetype();
/* Run exp->e2 semantic.
* Different from other binary expressions, the analysis of e2
* depends on the result of e1 in assignments.
*/
{
Expression *e2x = inferType(exp->e2, t1->baseElemOf());
e2x = expressionSemantic(e2x, sc);
e2x = resolveProperties(sc, e2x);
if (e2x->op == TOKtype)
e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684
if (e2x->op == TOKerror)
{
result = e2x;
return;
}
if (e2x->checkValue())
return setError();
exp->e2 = e2x;
}
/* Rewrite tuple assignment as a tuple of assignments.
*/
{
Expression *e2x = exp->e2;
Ltupleassign:
if (exp->e1->op == TOKtuple && e2x->op == TOKtuple)
{
TupleExp *tup1 = (TupleExp *)exp->e1;
TupleExp *tup2 = (TupleExp *)e2x;
size_t dim = tup1->exps->length;
Expression *e = NULL;
if (dim != tup2->exps->length)
{
exp->error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->length);
return setError();
}
if (dim == 0)
{
e = new IntegerExp(exp->loc, 0, Type::tint32);
e = new CastExp(exp->loc, e, Type::tvoid); // avoid "has no effect" error
e = Expression::combine(Expression::combine(tup1->e0, tup2->e0), e);
}
else
{
Expressions *exps = new Expressions;
exps->setDim(dim);
for (size_t i = 0; i < dim; i++)
{
Expression *ex1 = (*tup1->exps)[i];
Expression *ex2 = (*tup2->exps)[i];
(*exps)[i] = new AssignExp(exp->loc, ex1, ex2);
}
e = new TupleExp(exp->loc, Expression::combine(tup1->e0, tup2->e0), exps);
}
result = expressionSemantic(e, sc);
return;
}
/* Look for form: e1 = e2->aliasthis.
*/
if (exp->e1->op == TOKtuple)
{
TupleDeclaration *td = isAliasThisTuple(e2x);
if (!td)
goto Lnomatch;
assert(exp->e1->type->ty == Ttuple);
TypeTuple *tt = (TypeTuple *)exp->e1->type;
Expression *e0 = NULL;
Expression *ev = extractSideEffect(sc, "__tup", &e0, e2x);
Expressions *iexps = new Expressions();
iexps->push(ev);
for (size_t u = 0; u < iexps->length ; u++)
{
Lexpand:
Expression *e = (*iexps)[u];
Parameter *arg = Parameter::getNth(tt->arguments, u);
//printf("[%d] iexps->length = %d, ", u, iexps->length);
//printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars());
//printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars());
if (!arg || !e->type->implicitConvTo(arg->type))
{
// expand initializer to tuple
if (expandAliasThisTuples(iexps, u) != -1)
{
if (iexps->length <= u)
break;
goto Lexpand;
}
goto Lnomatch;
}
}
e2x = new TupleExp(e2x->loc, e0, iexps);
e2x = expressionSemantic(e2x, sc);
if (e2x->op == TOKerror)
{
result = e2x;
return;
}
// Do not need to overwrite exp->e2
goto Ltupleassign;
}
Lnomatch:
;
}
/* Inside constructor, if this is the first assignment of object field,
* rewrite this to initializing the field.
*/
if (exp->op == TOKassign && exp->e1->checkModifiable(sc) == 2)
{
//printf("[%s] change to init - %s\n", exp->loc.toChars(), toChars());
exp->op = TOKconstruct;
// Bugzilla 13515: set Index::modifiable flag for complex AA element initialization
if (exp->e1->op == TOKindex)
{
Expression *e1x = ((IndexExp *)exp->e1)->markSettingAAElem();
if (e1x->op == TOKerror)
{
result = e1x;
return;
}
}
}
else if (exp->op == TOKconstruct && exp->e1->op == TOKvar &&
((VarExp *)exp->e1)->var->storage_class & (STCout | STCref))
{
exp->memset |= referenceInit;
}
/* If it is an assignment from a 'foreign' type,
* check for operator overloading.
*/
if (exp->memset & referenceInit)
{
// If this is an initialization of a reference,
// do nothing
}
else if (t1->ty == Tstruct)
{
Expression *e1x = exp->e1;
Expression *e2x = exp->e2;
StructDeclaration *sd = ((TypeStruct *)t1)->sym;
if (exp->op == TOKconstruct)
{
Type *t2 = e2x->type->toBasetype();
if (t2->ty == Tstruct && sd == ((TypeStruct *)t2)->sym)
{
sd->size(exp->loc);
if (sd->sizeok != SIZEOKdone)
return setError();
if (!sd->ctor)
sd->ctor = sd->searchCtor();
// Bugzilla 15661: Look for the form from last of comma chain.
Expression *e2y = e2x;
while (e2y->op == TOKcomma)
e2y = ((CommaExp *)e2y)->e2;
CallExp *ce = (e2y->op == TOKcall) ? (CallExp *)e2y : NULL;
DotVarExp *dve = (ce && ce->e1->op == TOKdotvar)
? (DotVarExp *)ce->e1 : NULL;
if (sd->ctor && ce && dve && dve->var->isCtorDeclaration() &&
e2y->type->implicitConvTo(t1))
{
/* Look for form of constructor call which is:
* __ctmp.ctor(arguments...)
*/
/* Before calling the constructor, initialize
* variable with a bit copy of the default
* initializer
*/
AssignExp *ae = exp;
if (sd->zeroInit == 1 && !sd->isNested())
{
// Bugzilla 14606: Always use BlitExp for the special expression: (struct = 0)
ae = new BlitExp(ae->loc, ae->e1, new IntegerExp(exp->loc, 0, Type::tint32));
}
else
{
// Keep ae->op == TOKconstruct
ae->e2 = sd->isNested() ? t1->defaultInitLiteral(exp->loc) : t1->defaultInit(exp->loc);
}
ae->type = e1x->type;
/* Replace __ctmp being constructed with e1.
* We need to copy constructor call expression,
* because it may be used in other place.
*/
DotVarExp *dvx = (DotVarExp *)dve->copy();
dvx->e1 = e1x;
CallExp *cx = (CallExp *)ce->copy();
cx->e1 = dvx;
Expression *e0;
Expression::extractLast(e2x, &e0);
Expression *e = Expression::combine(ae, cx);
e = Expression::combine(e0, e);
e = expressionSemantic(e, sc);
result = e;
return;
}
if (sd->postblit)
{
/* We have a copy constructor for this
*/
if (e2x->op == TOKquestion)
{
/* Rewrite as:
* a ? e1 = b : e1 = c;
*/
CondExp *econd = (CondExp *)e2x;
Expression *ea1 = new ConstructExp(econd->e1->loc, e1x, econd->e1);
Expression *ea2 = new ConstructExp(econd->e1->loc, e1x, econd->e2);
Expression *e = new CondExp(exp->loc, econd->econd, ea1, ea2);
result = expressionSemantic(e, sc);
return;
}
if (e2x->isLvalue())
{
if (!e2x->type->implicitConvTo(e1x->type))
{
exp->error("conversion error from %s to %s", e2x->type->toChars(), e1x->type->toChars());
return setError();
}
/* Rewrite as:
* (e1 = e2).postblit();
*
* Blit assignment e1 = e2 returns a reference to the original e1,
* then call the postblit on it.
*/
Expression *e = e1x->copy();
e->type = e->type->mutableOf();
e = new BlitExp(exp->loc, e, e2x);
e = new DotVarExp(exp->loc, e, sd->postblit, false);
e = new CallExp(exp->loc, e);
result = expressionSemantic(e, sc);
return;
}
else
{
/* The struct value returned from the function is transferred
* so should not call the destructor on it.
*/
e2x = valueNoDtor(e2x);
}
}
}
else if (!e2x->implicitConvTo(t1))
{
sd->size(exp->loc);
if (sd->sizeok != SIZEOKdone)
return setError();
if (!sd->ctor)
sd->ctor = sd->searchCtor();
if (sd->ctor)
{
/* Look for implicit constructor call
* Rewrite as:
* e1 = init, e1.ctor(e2)
*/
Expression *einit;
einit = new BlitExp(exp->loc, e1x, e1x->type->defaultInit(exp->loc));
einit->type = e1x->type;
Expression *e;
e = new DotIdExp(exp->loc, e1x, Id::ctor);
e = new CallExp(exp->loc, e, e2x);
e = new CommaExp(exp->loc, einit, e);
e = expressionSemantic(e, sc);
result = e;
return;
}
if (search_function(sd, Id::call))
{
/* Look for static opCall
* (See bugzilla 2702 for more discussion)
* Rewrite as:
* e1 = typeof(e1).opCall(arguments)
*/
e2x = typeDotIdExp(e2x->loc, e1x->type, Id::call);
e2x = new CallExp(exp->loc, e2x, exp->e2);
e2x = expressionSemantic(e2x, sc);
e2x = resolveProperties(sc, e2x);
if (e2x->op == TOKerror)
{
result = e2x;
return;
}
if (e2x->checkValue())
return setError();
}
}
else // Bugzilla 11355
{
AggregateDeclaration *ad2 = isAggregate(e2x->type);
if (ad2 && ad2->aliasthis && !(exp->att2 && e2x->type == exp->att2))
{
if (!exp->att2 && exp->e2->type->checkAliasThisRec())
exp->att2 = exp->e2->type;
/* Rewrite (e1 op e2) as:
* (e1 op e2.aliasthis)
*/
exp->e2 = new DotIdExp(exp->e2->loc, exp->e2, ad2->aliasthis->ident);
result = expressionSemantic(exp, sc);
return;
}
}
}
else if (exp->op == TOKassign)
{
if (e1x->op == TOKindex &&
((IndexExp *)e1x)->e1->type->toBasetype()->ty == Taarray)
{
/*
* Rewrite:
* aa[key] = e2;
* as:
* ref __aatmp = aa;
* ref __aakey = key;
* ref __aaval = e2;
* (__aakey in __aatmp
* ? __aatmp[__aakey].opAssign(__aaval)
* : ConstructExp(__aatmp[__aakey], __aaval));
*/
IndexExp *ie = (IndexExp *)e1x;
Type *t2 = e2x->type->toBasetype();
Expression *e0 = NULL;
Expression *ea = extractSideEffect(sc, "__aatmp", &e0, ie->e1);
Expression *ek = extractSideEffect(sc, "__aakey", &e0, ie->e2);
Expression *ev = extractSideEffect(sc, "__aaval", &e0, e2x);
AssignExp *ae = (AssignExp *)exp->copy();
ae->e1 = new IndexExp(exp->loc, ea, ek);
ae->e1 = expressionSemantic(ae->e1, sc);
ae->e1 = ae->e1->optimize(WANTvalue);
ae->e2 = ev;
Expression *e = ae->op_overload(sc);
if (e)
{
Expression *ey = NULL;
if (t2->ty == Tstruct && sd == t2->toDsymbol(sc))
{
ey = ev;
}
else if (!ev->implicitConvTo(ie->type) && sd->ctor)
{
// Look for implicit constructor call
// Rewrite as S().ctor(e2)
ey = new StructLiteralExp(exp->loc, sd, NULL);
ey = new DotIdExp(exp->loc, ey, Id::ctor);
ey = new CallExp(exp->loc, ey, ev);
ey = trySemantic(ey, sc);
}
if (ey)
{
Expression *ex;
ex = new IndexExp(exp->loc, ea, ek);
ex = expressionSemantic(ex, sc);
ex = ex->optimize(WANTvalue);
ex = ex->modifiableLvalue(sc, ex); // allocate new slot
ey = new ConstructExp(exp->loc, ex, ey);
ey = expressionSemantic(ey, sc);
if (ey->op == TOKerror)
{
result = ey;
return;
}
ex = e;
// Bugzilla 14144: The whole expression should have the common type
// of opAssign() return and assigned AA entry.
// Even if there's no common type, expression should be typed as void.
Type *t = NULL;
if (!typeMerge(sc, TOKquestion, &t, &ex, &ey))
{
ex = new CastExp(ex->loc, ex, Type::tvoid);
ey = new CastExp(ey->loc, ey, Type::tvoid);
}
e = new CondExp(exp->loc, new InExp(exp->loc, ek, ea), ex, ey);
}
e = Expression::combine(e0, e);
e = expressionSemantic(e, sc);
result = e;
return;
}
}
else
{
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
}
}
else
assert(exp->op == TOKblit);
exp->e1 = e1x;
exp->e2 = e2x;
}
else if (t1->ty == Tclass)
{
// Disallow assignment operator overloads for same type
if (exp->op == TOKassign && !exp->e2->implicitConvTo(exp->e1->type))
{
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
}
}
else if (t1->ty == Tsarray)
{
// SliceExp cannot have static array type without context inference.
assert(exp->e1->op != TOKslice);
Expression *e1x = exp->e1;
Expression *e2x = exp->e2;
if (e2x->implicitConvTo(e1x->type))
{
if (exp->op != TOKblit &&
((e2x->op == TOKslice && ((UnaExp *)e2x)->e1->isLvalue()) ||
(e2x->op == TOKcast && ((UnaExp *)e2x)->e1->isLvalue()) ||
(e2x->op != TOKslice && e2x->isLvalue())))
{
if (e1x->checkPostblit(sc, t1))
return setError();
}
// e2 matches to t1 because of the implicit length match, so
if (isUnaArrayOp(e2x->op) || isBinArrayOp(e2x->op))
{
// convert e1 to e1[]
// e.g. e1[] = a[] + b[];
SliceExp *sle = new SliceExp(e1x->loc, e1x, NULL, NULL);
sle->arrayop = true;
e1x = expressionSemantic(sle, sc);
}
else
{
// convert e2 to t1 later
// e.g. e1 = [1, 2, 3];
}
}
else
{
if (e2x->implicitConvTo(t1->nextOf()->arrayOf()) > MATCHnomatch)
{
uinteger_t dim1 = ((TypeSArray *)t1)->dim->toInteger();
uinteger_t dim2 = dim1;
if (e2x->op == TOKarrayliteral)
{
ArrayLiteralExp *ale = (ArrayLiteralExp *)e2x;
dim2 = ale->elements ? ale->elements->length : 0;
}
else if (e2x->op == TOKslice)
{
Type *tx = toStaticArrayType((SliceExp *)e2x);
if (tx)
dim2 = ((TypeSArray *)tx)->dim->toInteger();
}
if (dim1 != dim2)
{
exp->error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2);
return setError();
}
}
// May be block or element-wise assignment, so
// convert e1 to e1[]
if (exp->op != TOKassign)
{
// If multidimensional static array, treat as one large array
dinteger_t dim = t1->numberOfElems(exp->loc);
e1x->type = t1->baseElemOf()->sarrayOf(dim);
}
SliceExp *sle = new SliceExp(e1x->loc, e1x, NULL, NULL);
sle->arrayop = true;
e1x = expressionSemantic(sle, sc);
}
if (e1x->op == TOKerror)
{
result = e1x;
return;
}
if (e2x->op == TOKerror)
{
result = e2x;
return;
}
exp->e1 = e1x;
exp->e2 = e2x;
t1 = e1x->type->toBasetype();
}
/* Check the mutability of e1.
*/
if (exp->e1->op == TOKarraylength)
{
// e1 is not an lvalue, but we let code generator handle it
ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1;
Expression *ale1x = ale->e1;
ale1x = ale1x->modifiableLvalue(sc, exp->e1);
if (ale1x->op == TOKerror)
{
result = ale1x;
return;
}
ale->e1 = ale1x;
Type *tn = ale->e1->type->toBasetype()->nextOf();
checkDefCtor(ale->loc, tn);
semanticTypeInfo(sc, tn);
}
else if (exp->e1->op == TOKslice)
{
Type *tn = exp->e1->type->nextOf();
if (exp->op == TOKassign && !tn->isMutable())
{
exp->error("slice %s is not mutable", exp->e1->toChars());
return setError();
}
// For conditional operator, both branches need conversion.
SliceExp *se = (SliceExp *)exp->e1;
while (se->e1->op == TOKslice)
se = (SliceExp *)se->e1;
if (se->e1->op == TOKquestion &&
se->e1->type->toBasetype()->ty == Tsarray)
{
se->e1 = se->e1->modifiableLvalue(sc, exp->e1);
if (se->e1->op == TOKerror)
{
result = se->e1;
return;
}
}
}
else
{
Expression *e1x = exp->e1;
// Try to do a decent error message with the expression
// before it got constant folded
if (e1x->op != TOKvar)
e1x = e1x->optimize(WANTvalue);
if (exp->op == TOKassign)
e1x = e1x->modifiableLvalue(sc, e1old);
if (e1x->op == TOKerror)
{
result = e1x;
return;
}
exp->e1 = e1x;
}
/* Tweak e2 based on the type of e1.
*/
Expression *e2x = exp->e2;
Type *t2 = e2x->type->toBasetype();
// If it is a array, get the element type. Note that it may be
// multi-dimensional.
Type *telem = t1;
while (telem->ty == Tarray)
telem = telem->nextOf();
if (exp->e1->op == TOKslice &&
t1->nextOf() && (telem->ty != Tvoid || e2x->op == TOKnull) &&
e2x->implicitConvTo(t1->nextOf())
)
{
// Check for block assignment. If it is of type void[], void[][], etc,
// '= null' is the only allowable block assignment (Bug 7493)
// memset
exp->memset |= blockAssign; // make it easy for back end to tell what this is
e2x = e2x->implicitCastTo(sc, t1->nextOf());
if (exp->op != TOKblit && e2x->isLvalue() &&
exp->e1->checkPostblit(sc, t1->nextOf()))
return setError();
}
else if (exp->e1->op == TOKslice &&
(t2->ty == Tarray || t2->ty == Tsarray) &&
t2->nextOf()->implicitConvTo(t1->nextOf()))
{
// Check element-wise assignment.
/* If assigned elements number is known at compile time,
* check the mismatch.
*/
SliceExp *se1 = (SliceExp *)exp->e1;
TypeSArray *tsa1 = (TypeSArray *)toStaticArrayType(se1);
TypeSArray *tsa2 = NULL;
if (e2x->op == TOKarrayliteral)
tsa2 = (TypeSArray *)t2->nextOf()->sarrayOf(((ArrayLiteralExp *)e2x)->elements->length);
else if (e2x->op == TOKslice)
tsa2 = (TypeSArray *)toStaticArrayType((SliceExp *)e2x);
else if (t2->ty == Tsarray)
tsa2 = (TypeSArray *)t2;
if (tsa1 && tsa2)
{
uinteger_t dim1 = tsa1->dim->toInteger();
uinteger_t dim2 = tsa2->dim->toInteger();
if (dim1 != dim2)
{
exp->error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2);
return setError();
}
}
if (exp->op != TOKblit &&
((e2x->op == TOKslice && ((UnaExp *)e2x)->e1->isLvalue()) ||
(e2x->op == TOKcast && ((UnaExp *)e2x)->e1->isLvalue()) ||
(e2x->op != TOKslice && e2x->isLvalue())))
{
if (exp->e1->checkPostblit(sc, t1->nextOf()))
return setError();
}
if (0 && global.params.warnings != DIAGNOSTICoff && !global.gag && exp->op == TOKassign &&
e2x->op != TOKslice && e2x->op != TOKassign &&
e2x->op != TOKarrayliteral && e2x->op != TOKstring &&
!(e2x->op == TOKadd || e2x->op == TOKmin ||
e2x->op == TOKmul || e2x->op == TOKdiv ||
e2x->op == TOKmod || e2x->op == TOKxor ||
e2x->op == TOKand || e2x->op == TOKor ||
e2x->op == TOKpow ||
e2x->op == TOKtilde || e2x->op == TOKneg))
{
const char* e1str = exp->e1->toChars();
const char* e2str = e2x->toChars();
exp->warning("explicit element-wise assignment %s = (%s)[] is better than %s = %s",
e1str, e2str, e1str, e2str);
}
Type *t2n = t2->nextOf();
Type *t1n = t1->nextOf();
int offset;
if (t2n->equivalent(t1n) ||
(t1n->isBaseOf(t2n, &offset) && offset == 0))
{
/* Allow copy of distinct qualifier elements.
* eg.
* char[] dst; const(char)[] src;
* dst[] = src;
*
* class C {} class D : C {}
* C[2] ca; D[] da;
* ca[] = da;
*/
if (isArrayOpValid(e2x))
{
// Don't add CastExp to keep AST for array operations
e2x = e2x->copy();
e2x->type = exp->e1->type->constOf();
}
else
e2x = e2x->castTo(sc, exp->e1->type->constOf());
}
else
{
/* Bugzilla 15778: A string literal has an array type of immutable
* elements by default, and normally it cannot be convertible to
* array type of mutable elements. But for element-wise assignment,
* elements need to be const at best. So we should give a chance
* to change code unit size for polysemous string literal.
*/
if (e2x->op == TOKstring)
e2x = e2x->implicitCastTo(sc, exp->e1->type->constOf());
else
e2x = e2x->implicitCastTo(sc, exp->e1->type);
}
if (t1n->toBasetype()->ty == Tvoid && t2n->toBasetype()->ty == Tvoid)
{
if (!sc->intypeof && sc->func && sc->func->setUnsafe())
{
exp->error("cannot copy void[] to void[] in @safe code");
return setError();
}
}
}
else
{
if (0 && global.params.warnings != DIAGNOSTICoff && !global.gag && exp->op == TOKassign &&
t1->ty == Tarray && t2->ty == Tsarray &&
e2x->op != TOKslice &&
t2->implicitConvTo(t1))
{ // Disallow ar[] = sa (Converted to ar[] = sa[])
// Disallow da = sa (Converted to da = sa[])
const char* e1str = exp->e1->toChars();
const char* e2str = e2x->toChars();
const char* atypestr = exp->e1->op == TOKslice ? "element-wise" : "slice";
exp->warning("explicit %s assignment %s = (%s)[] is better than %s = %s",
atypestr, e1str, e2str, e1str, e2str);
}
if (exp->op == TOKblit)
e2x = e2x->castTo(sc, exp->e1->type);
else
e2x = e2x->implicitCastTo(sc, exp->e1->type);
}
if (e2x->op == TOKerror)
{
result = e2x;
return;
}
exp->e2 = e2x;
t2 = exp->e2->type->toBasetype();
/* Look for array operations
*/
if ((t2->ty == Tarray || t2->ty == Tsarray) && isArrayOpValid(exp->e2))
{
// Look for valid array operations
if (!(exp->memset & blockAssign) && exp->e1->op == TOKslice &&
(isUnaArrayOp(exp->e2->op) || isBinArrayOp(exp->e2->op)))
{
exp->type = exp->e1->type;
if (exp->op == TOKconstruct) // Bugzilla 10282: tweak mutability of e1 element
exp->e1->type = exp->e1->type->nextOf()->mutableOf()->arrayOf();
result = arrayOp(exp, sc);
return;
}
// Drop invalid array operations in e2
// d = a[] + b[], d = (a[] + b[])[0..2], etc
if (checkNonAssignmentArrayOp(exp->e2, !(exp->memset & blockAssign) && exp->op == TOKassign))
return setError();
// Remains valid array assignments
// d = d[], d = [1,2,3], etc
}
/* Don't allow assignment to classes that were allocated on the stack with:
* scope Class c = new Class();
*/
if (exp->e1->op == TOKvar && exp->op == TOKassign)
{
VarExp *ve = (VarExp *)exp->e1;
VarDeclaration *vd = ve->var->isVarDeclaration();
if (vd && (vd->onstack || vd->mynew))
{
assert(t1->ty == Tclass);
exp->error("cannot rebind scope variables");
}
}
if (exp->e1->op == TOKvar && ((VarExp *)exp->e1)->var->ident == Id::ctfe)
{
exp->error("cannot modify compiler-generated variable __ctfe");
}
exp->type = exp->e1->type;
assert(exp->type);
Expression *res = exp->op == TOKassign ? exp->reorderSettingAAElem(sc) : exp;
checkAssignEscape(sc, res, false);
result = res;
}
void visit(CatAssignExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
//printf("CatAssignExp::semantic() %s\n", toChars());
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->e1->op == TOKslice)
{
SliceExp *se = (SliceExp *)exp->e1;
if (se->e1->type->toBasetype()->ty == Tsarray)
{
exp->error("cannot append to static array %s", se->e1->type->toChars());
return setError();
}
}
exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
if (exp->e1->op == TOKerror)
{
result = exp->e1;
return;
}
if (exp->e2->op == TOKerror)
{
result = exp->e2;
return;
}
if (checkNonAssignmentArrayOp(exp->e2))
return setError();
Type *tb1 = exp->e1->type->toBasetype();
Type *tb1next = tb1->nextOf();
Type *tb2 = exp->e2->type->toBasetype();
if ((tb1->ty == Tarray) &&
(tb2->ty == Tarray || tb2->ty == Tsarray) &&
(exp->e2->implicitConvTo(exp->e1->type)
|| (tb2->nextOf()->implicitConvTo(tb1next) &&
(tb2->nextOf()->size(Loc()) == tb1next->size(Loc())))
)
)
{
// Append array
if (exp->e1->checkPostblit(sc, tb1next))
return setError();
exp->e2 = exp->e2->castTo(sc, exp->e1->type);
}
else if ((tb1->ty == Tarray) &&
exp->e2->implicitConvTo(tb1next)
)
{
// Append element
if (exp->e2->checkPostblit(sc, tb2))
return setError();
exp->e2 = exp->e2->castTo(sc, tb1next);
exp->e2 = doCopyOrMove(sc, exp->e2);
}
else if (tb1->ty == Tarray &&
(tb1next->ty == Tchar || tb1next->ty == Twchar) &&
exp->e2->type->ty != tb1next->ty &&
exp->e2->implicitConvTo(Type::tdchar)
)
{ // Append dchar to char[] or wchar[]
exp->e2 = exp->e2->castTo(sc, Type::tdchar);
/* Do not allow appending wchar to char[] because if wchar happens
* to be a surrogate pair, nothing good can result.
*/
}
else
{
exp->error("cannot append type %s to type %s", tb2->toChars(), tb1->toChars());
return setError();
}
if (exp->e2->checkValue())
return setError();
exp->type = exp->e1->type;
result = exp->reorderSettingAAElem(sc);
}
void visit(PowAssignExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->e1->checkReadModifyWrite(exp->op, exp->e2))
return setError();
assert(exp->e1->type && exp->e2->type);
if (exp->e1->op == TOKslice || exp->e1->type->ty == Tarray || exp->e1->type->ty == Tsarray)
{
if (checkNonAssignmentArrayOp(exp->e1))
return setError();
// T[] ^^= ...
if (exp->e2->implicitConvTo(exp->e1->type->nextOf()))
{
// T[] ^^= T
exp->e2 = exp->e2->castTo(sc, exp->e1->type->nextOf());
}
else if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
// Check element types are arithmetic
Type *tb1 = exp->e1->type->nextOf()->toBasetype();
Type *tb2 = exp->e2->type->toBasetype();
if (tb2->ty == Tarray || tb2->ty == Tsarray)
tb2 = tb2->nextOf()->toBasetype();
if ( (tb1->isintegral() || tb1->isfloating()) &&
(tb2->isintegral() || tb2->isfloating()))
{
exp->type = exp->e1->type;
result = arrayOp(exp, sc);
return;
}
}
else
{
exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1);
}
if ((exp->e1->type->isintegral() || exp->e1->type->isfloating()) &&
(exp->e2->type->isintegral() || exp->e2->type->isfloating()))
{
Expression *e0 = NULL;
e = exp->reorderSettingAAElem(sc);
e = Expression::extractLast(e, &e0);
assert(e == exp);
if (exp->e1->op == TOKvar)
{
// Rewrite: e1 = e1 ^^ e2
e = new PowExp(exp->loc, exp->e1->syntaxCopy(), exp->e2);
e = new AssignExp(exp->loc, exp->e1, e);
}
else
{
// Rewrite: ref tmp = e1; tmp = tmp ^^ e2
VarDeclaration *v = copyToTemp(STCref, "__powtmp", exp->e1);
Expression *de = new DeclarationExp(exp->e1->loc, v);
VarExp *ve = new VarExp(exp->e1->loc, v);
e = new PowExp(exp->loc, ve, exp->e2);
e = new AssignExp(exp->loc, new VarExp(exp->e1->loc, v), e);
e = new CommaExp(exp->loc, de, e);
}
e = Expression::combine(e0, e);
e = expressionSemantic(e, sc);
result = e;
return;
}
result = exp->incompatibleTypes();
}
void visit(AddExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
Type *tb1 = exp->e1->type->toBasetype();
Type *tb2 = exp->e2->type->toBasetype();
bool err = false;
if (tb1->ty == Tdelegate ||
(tb1->ty == Tpointer && tb1->nextOf()->ty == Tfunction))
{
err |= exp->e1->checkArithmetic();
}
if (tb2->ty == Tdelegate ||
(tb2->ty == Tpointer && tb2->nextOf()->ty == Tfunction))
{
err |= exp->e2->checkArithmetic();
}
if (err)
return setError();
if ((tb1->ty == Tpointer && exp->e2->type->isintegral()) ||
(tb2->ty == Tpointer && exp->e1->type->isintegral()))
{
result = scaleFactor(exp, sc);
return;
}
if (tb1->ty == Tpointer && tb2->ty == Tpointer)
{
result = exp->incompatibleTypes();
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
tb1 = exp->e1->type->toBasetype();
if (!target.isVectorOpSupported(tb1, exp->op, tb2))
{
result = exp->incompatibleTypes();
return;
}
if ((tb1->isreal() && exp->e2->type->isimaginary()) ||
(tb1->isimaginary() && exp->e2->type->isreal()))
{
switch (exp->type->toBasetype()->ty)
{
case Tfloat32:
case Timaginary32:
exp->type = Type::tcomplex32;
break;
case Tfloat64:
case Timaginary64:
exp->type = Type::tcomplex64;
break;
case Tfloat80:
case Timaginary80:
exp->type = Type::tcomplex80;
break;
default:
assert(0);
}
}
result = exp;
}
void visit(MinExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
Type *t1 = exp->e1->type->toBasetype();
Type *t2 = exp->e2->type->toBasetype();
bool err = false;
if (t1->ty == Tdelegate ||
(t1->ty == Tpointer && t1->nextOf()->ty == Tfunction))
{
err |= exp->e1->checkArithmetic();
}
if (t2->ty == Tdelegate ||
(t2->ty == Tpointer && t2->nextOf()->ty == Tfunction))
{
err |= exp->e2->checkArithmetic();
}
if (err)
return setError();
if (t1->ty == Tpointer)
{
if (t2->ty == Tpointer)
{
// Need to divide the result by the stride
// Replace (ptr - ptr) with (ptr - ptr) / stride
d_int64 stride;
// make sure pointer types are compatible
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
exp->type = Type::tptrdiff_t;
stride = t2->nextOf()->size();
if (stride == 0)
{
e = new IntegerExp(exp->loc, 0, Type::tptrdiff_t);
}
else
{
e = new DivExp(exp->loc, exp, new IntegerExp(Loc(), stride, Type::tptrdiff_t));
e->type = Type::tptrdiff_t;
}
}
else if (t2->isintegral())
e = scaleFactor(exp, sc);
else
{
exp->error("can't subtract %s from pointer", t2->toChars());
e = new ErrorExp();
}
result = e;
return;
}
if (t2->ty == Tpointer)
{
exp->type = exp->e2->type;
exp->error("can't subtract pointer from %s", exp->e1->type->toChars());
return setError();
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
t1 = exp->e1->type->toBasetype();
t2 = exp->e2->type->toBasetype();
if (!target.isVectorOpSupported(t1, exp->op, t2))
{
result = exp->incompatibleTypes();
return;
}
if ((t1->isreal() && t2->isimaginary()) ||
(t1->isimaginary() && t2->isreal()))
{
switch (exp->type->ty)
{
case Tfloat32:
case Timaginary32:
exp->type = Type::tcomplex32;
break;
case Tfloat64:
case Timaginary64:
exp->type = Type::tcomplex64;
break;
case Tfloat80:
case Timaginary80:
exp->type = Type::tcomplex80;
break;
default:
assert(0);
}
}
result = exp;
}
void visit(CatExp *exp)
{
//printf("CatExp::semantic() %s\n", exp->toChars());
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
Type *tb1 = exp->e1->type->toBasetype();
Type *tb2 = exp->e2->type->toBasetype();
bool f1 = checkNonAssignmentArrayOp(exp->e1);
bool f2 = checkNonAssignmentArrayOp(exp->e2);
if (f1 || f2)
return setError();
/* BUG: Should handle things like:
* char c;
* c ~ ' '
* ' ' ~ c;
*/
Type *tb1next = tb1->nextOf();
Type *tb2next = tb2->nextOf();
// Check for: array ~ array
if (tb1next && tb2next &&
(tb1next->implicitConvTo(tb2next) >= MATCHconst ||
tb2next->implicitConvTo(tb1next) >= MATCHconst ||
(exp->e1->op == TOKarrayliteral && exp->e1->implicitConvTo(tb2)) ||
(exp->e2->op == TOKarrayliteral && exp->e2->implicitConvTo(tb1))
)
)
{
/* Bugzilla 9248: Here to avoid the case of:
* void*[] a = [cast(void*)1];
* void*[] b = [cast(void*)2];
* a ~ b;
* becoming:
* a ~ [cast(void*)b];
*/
/* Bugzilla 14682: Also to avoid the case of:
* int[][] a;
* a ~ [];
* becoming:
* a ~ cast(int[])[];
*/
goto Lpeer;
}
// Check for: array ~ element
if ((tb1->ty == Tsarray || tb1->ty == Tarray) && tb2->ty != Tvoid)
{
if (exp->e1->op == TOKarrayliteral)
{
exp->e2 = exp->e2->isLvalue() ? callCpCtor(sc, exp->e2) : valueNoDtor(exp->e2);
// Bugzilla 14686: Postblit call appears in AST, and this is
// finally translated to an ArrayLiteralExp in below otpimize().
}
else if (exp->e1->op == TOKstring)
{
// No postblit call exists on character (integer) value.
}
else
{
if (exp->e2->checkPostblit(sc, tb2))
return setError();
// Postblit call will be done in runtime helper function
}
if (exp->e1->op == TOKarrayliteral && exp->e1->implicitConvTo(tb2->arrayOf()))
{
exp->e1 = exp->e1->implicitCastTo(sc, tb2->arrayOf());
exp->type = tb2->arrayOf();
goto L2elem;
}
if (exp->e2->implicitConvTo(tb1next) >= MATCHconvert)
{
exp->e2 = exp->e2->implicitCastTo(sc, tb1next);
exp->type = tb1next->arrayOf();
L2elem:
if (tb2->ty == Tarray || tb2->ty == Tsarray)
{
// Make e2 into [e2]
exp->e2 = new ArrayLiteralExp(exp->e2->loc, exp->type, exp->e2);
}
result = exp->optimize(WANTvalue);
return;
}
}
// Check for: element ~ array
if ((tb2->ty == Tsarray || tb2->ty == Tarray) && tb1->ty != Tvoid)
{
if (exp->e2->op == TOKarrayliteral)
{
exp->e1 = exp->e1->isLvalue() ? callCpCtor(sc, exp->e1) : valueNoDtor(exp->e1);
}
else if (exp->e2->op == TOKstring)
{
}
else
{
if (exp->e1->checkPostblit(sc, tb1))
return setError();
}
if (exp->e2->op == TOKarrayliteral && exp->e2->implicitConvTo(tb1->arrayOf()))
{
exp->e2 = exp->e2->implicitCastTo(sc, tb1->arrayOf());
exp->type = tb1->arrayOf();
goto L1elem;
}
if (exp->e1->implicitConvTo(tb2next) >= MATCHconvert)
{
exp->e1 = exp->e1->implicitCastTo(sc, tb2next);
exp->type = tb2next->arrayOf();
L1elem:
if (tb1->ty == Tarray || tb1->ty == Tsarray)
{
// Make e1 into [e1]
exp->e1 = new ArrayLiteralExp(exp->e1->loc, exp->type, exp->e1);
}
result = exp->optimize(WANTvalue);
return;
}
}
Lpeer:
if ((tb1->ty == Tsarray || tb1->ty == Tarray) &&
(tb2->ty == Tsarray || tb2->ty == Tarray) &&
(tb1next->mod || tb2next->mod) &&
(tb1next->mod != tb2next->mod)
)
{
Type *t1 = tb1next->mutableOf()->constOf()->arrayOf();
Type *t2 = tb2next->mutableOf()->constOf()->arrayOf();
if (exp->e1->op == TOKstring && !((StringExp *)exp->e1)->committed)
exp->e1->type = t1;
else
exp->e1 = exp->e1->castTo(sc, t1);
if (exp->e2->op == TOKstring && !((StringExp *)exp->e2)->committed)
exp->e2->type = t2;
else
exp->e2 = exp->e2->castTo(sc, t2);
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
exp->type = exp->type->toHeadMutable();
Type *tb = exp->type->toBasetype();
if (tb->ty == Tsarray)
exp->type = tb->nextOf()->arrayOf();
if (exp->type->ty == Tarray && tb1next && tb2next &&
tb1next->mod != tb2next->mod)
{
exp->type = exp->type->nextOf()->toHeadMutable()->arrayOf();
}
if (Type *tbn = tb->nextOf())
{
if (exp->checkPostblit(sc, tbn))
return setError();
}
Type *t1 = exp->e1->type->toBasetype();
Type *t2 = exp->e2->type->toBasetype();
if ((t1->ty == Tarray || t1->ty == Tsarray) &&
(t2->ty == Tarray || t2->ty == Tsarray))
{
// Normalize to ArrayLiteralExp or StringExp as far as possible
e = exp->optimize(WANTvalue);
}
else
{
//printf("(%s) ~ (%s)\n", exp->e1->toChars(), exp->e2->toChars());
result = exp->incompatibleTypes();
return;
}
result = e;
}
void visit(MulExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (exp->checkArithmeticBin())
return setError();
if (exp->type->isfloating())
{
Type *t1 = exp->e1->type;
Type *t2 = exp->e2->type;
if (t1->isreal())
{
exp->type = t2;
}
else if (t2->isreal())
{
exp->type = t1;
}
else if (t1->isimaginary())
{
if (t2->isimaginary())
{
switch (t1->toBasetype()->ty)
{
case Timaginary32:
exp->type = Type::tfloat32;
break;
case Timaginary64:
exp->type = Type::tfloat64;
break;
case Timaginary80:
exp->type = Type::tfloat80;
break;
default:
assert(0);
}
// iy * iv = -yv
exp->e1->type = exp->type;
exp->e2->type = exp->type;
e = new NegExp(exp->loc, exp);
e = expressionSemantic(e, sc);
result = e;
return;
}
else
exp->type = t2; // t2 is complex
}
else if (t2->isimaginary())
{
exp->type = t1; // t1 is complex
}
}
else if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
result = exp;
}
void visit(DivExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (exp->checkArithmeticBin())
return setError();
if (exp->type->isfloating())
{
Type *t1 = exp->e1->type;
Type *t2 = exp->e2->type;
if (t1->isreal())
{
exp->type = t2;
if (t2->isimaginary())
{
// x/iv = i(-x/v)
exp->e2->type = t1;
e = new NegExp(exp->loc, exp);
e = expressionSemantic(e, sc);
result = e;
return;
}
}
else if (t2->isreal())
{
exp->type = t1;
}
else if (t1->isimaginary())
{
if (t2->isimaginary())
{
switch (t1->toBasetype()->ty)
{
case Timaginary32:
exp->type = Type::tfloat32;
break;
case Timaginary64:
exp->type = Type::tfloat64;
break;
case Timaginary80:
exp->type = Type::tfloat80;
break;
default:
assert(0);
}
}
else
exp->type = t2; // t2 is complex
}
else if (t2->isimaginary())
{
exp->type = t1; // t1 is complex
}
}
else if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
result = exp;
}
void visit(ModExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
if (exp->checkArithmeticBin())
return setError();
if (exp->type->isfloating())
{
exp->type = exp->e1->type;
if (exp->e2->type->iscomplex())
{
exp->error("cannot perform modulo complex arithmetic");
return setError();
}
}
result = exp;
}
void visit(PowExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
//printf("PowExp::semantic() %s\n", exp->toChars());
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (exp->checkArithmeticBin())
return setError();
if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
// For built-in numeric types, there are several cases.
// TODO: backend support, especially for e1 ^^ 2.
// First, attempt to fold the expression.
e = exp->optimize(WANTvalue);
if (e->op != TOKpow)
{
e = expressionSemantic(e, sc);
result = e;
return;
}
// Determine if we're raising to an integer power.
sinteger_t intpow = 0;
if (exp->e2->op == TOKint64 && ((sinteger_t)exp->e2->toInteger() == 2 || (sinteger_t)exp->e2->toInteger() == 3))
intpow = exp->e2->toInteger();
else if (exp->e2->op == TOKfloat64 && (exp->e2->toReal() == ldouble((sinteger_t)exp->e2->toReal())))
intpow = (sinteger_t)(exp->e2->toReal());
// Deal with x^^2, x^^3 immediately, since they are of practical importance.
if (intpow == 2 || intpow == 3)
{
// Replace x^^2 with (tmp = x, tmp*tmp)
// Replace x^^3 with (tmp = x, tmp*tmp*tmp)
VarDeclaration *tmp = copyToTemp(0, "__powtmp", exp->e1);
Expression *de = new DeclarationExp(exp->loc, tmp);
Expression *ve = new VarExp(exp->loc, tmp);
/* Note that we're reusing ve. This should be ok.
*/
Expression *me = new MulExp(exp->loc, ve, ve);
if (intpow == 3)
me = new MulExp(exp->loc, me, ve);
e = new CommaExp(exp->loc, de, me);
e = expressionSemantic(e, sc);
result = e;
return;
}
Module *mmath = loadStdMath();
if (!mmath)
{
//exp->error("requires std.math for ^^ operators");
//fatal();
// Leave handling of PowExp to the backend, or throw
// an error gracefully if no backend support exists.
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
result = exp;
return;
}
e = new ScopeExp(exp->loc, mmath);
if (exp->e2->op == TOKfloat64 && exp->e2->toReal() == CTFloat::half)
{
// Replace e1 ^^ 0.5 with .std.math.sqrt(x)
e = new CallExp(exp->loc, new DotIdExp(exp->loc, e, Id::_sqrt), exp->e1);
}
else
{
// Replace e1 ^^ e2 with .std.math.pow(e1, e2)
e = new CallExp(exp->loc, new DotIdExp(exp->loc, e, Id::_pow), exp->e1, exp->e2);
}
e = expressionSemantic(e, sc);
result = e;
}
void visit(ShlExp *exp)
{
//printf("ShlExp::semantic(), type = %p\n", exp->type);
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->checkIntegralBin())
return setError();
if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
exp->e1 = integralPromotions(exp->e1, sc);
if (exp->e2->type->toBasetype()->ty != Tvector)
exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
exp->type = exp->e1->type;
result = exp;
}
void visit(ShrExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->checkIntegralBin())
return setError();
if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
exp->e1 = integralPromotions(exp->e1, sc);
if (exp->e2->type->toBasetype()->ty != Tvector)
exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
exp->type = exp->e1->type;
result = exp;
}
void visit(UshrExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->checkIntegralBin())
return setError();
if (!target.isVectorOpSupported(exp->e1->type->toBasetype(), exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
exp->e1 = integralPromotions(exp->e1, sc);
if (exp->e2->type->toBasetype()->ty != Tvector)
exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt);
exp->type = exp->e1->type;
result = exp;
}
void visit(AndExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->e1->type->toBasetype()->ty == Tbool &&
exp->e2->type->toBasetype()->ty == Tbool)
{
exp->type = exp->e1->type;
result = exp;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
if (exp->checkIntegralBin())
return setError();
result = exp;
}
void visit(OrExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->e1->type->toBasetype()->ty == Tbool &&
exp->e2->type->toBasetype()->ty == Tbool)
{
exp->type = exp->e1->type;
result = exp;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
if (exp->checkIntegralBin())
return setError();
result = exp;
}
void visit(XorExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
if (exp->e1->type->toBasetype()->ty == Tbool &&
exp->e2->type->toBasetype()->ty == Tbool)
{
exp->type = exp->e1->type;
result = exp;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
Type *tb = exp->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (!isArrayOpValid(exp))
{
exp->error("invalid array operation %s (possible missing [])", exp->toChars());
return setError();
}
result = exp;
return;
}
if (!target.isVectorOpSupported(tb, exp->op, exp->e2->type->toBasetype()))
{
result = exp->incompatibleTypes();
return;
}
if (exp->checkIntegralBin())
return setError();
result = exp;
}
void visit(LogicalExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
setNoderefOperands(exp);
Expression *e1x = expressionSemantic(exp->e1, sc);
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
if (e1x->op == TOKtype)
e1x = resolveAliasThis(sc, e1x);
e1x = resolveProperties(sc, e1x);
e1x = e1x->toBoolean(sc);
unsigned cs1 = sc->callSuper;
if (sc->flags & SCOPEcondition)
{
/* If in static if, don't evaluate e2 if we don't have to.
*/
e1x = e1x->optimize(WANTvalue);
if (e1x->isBool(exp->op == TOKoror))
{
result = new IntegerExp(exp->loc, exp->op == TOKoror, Type::tbool);
return;
}
}
Expression *e2x = expressionSemantic(exp->e2, sc);
sc->mergeCallSuper(exp->loc, cs1);
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
if (e2x->op == TOKtype)
e2x = resolveAliasThis(sc, e2x);
e2x = resolveProperties(sc, e2x);
bool f1 = checkNonAssignmentArrayOp(e1x);
bool f2 = checkNonAssignmentArrayOp(e2x);
if (f1 || f2)
return setError();
// Unless the right operand is 'void', the expression is converted to 'bool'.
if (e2x->type->ty != Tvoid)
e2x = e2x->toBoolean(sc);
if (e2x->op == TOKtype || e2x->op == TOKscope)
{
exp->error("%s is not an expression", exp->e2->toChars());
return setError();
}
if (e1x->op == TOKerror || e1x->type->ty == Tnoreturn)
{
result = e1x;
return;
}
if (e2x->op == TOKerror)
{
result = e2x;
return;
}
// The result type is 'bool', unless the right operand has type 'void'.
if (e2x->type->ty == Tvoid)
exp->type = Type::tvoid;
else
exp->type = Type::tbool;
exp->e1 = e1x;
exp->e2 = e2x;
result = exp;
}
void visit(InExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Expression *e = exp->op_overload(sc);
if (e)
{
result = e;
return;
}
Type *t2b = exp->e2->type->toBasetype();
switch (t2b->ty)
{
case Taarray:
{
TypeAArray *ta = (TypeAArray *)t2b;
// Special handling for array keys
if (!arrayTypeCompatible(exp->e1->loc, exp->e1->type, ta->index))
{
// Convert key to type of key
exp->e1 = exp->e1->implicitCastTo(sc, ta->index);
}
semanticTypeInfo(sc, ta->index);
// Return type is pointer to value
exp->type = ta->nextOf()->pointerTo();
break;
}
default:
result = exp->incompatibleTypes();
return;
case Terror:
return setError();
}
result = exp;
}
void visit(RemoveExp *e)
{
if (Expression *ex = binSemantic(e, sc))
{
result = ex;
return;
}
result = e;
}
void visit(CmpExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
setNoderefOperands(exp);
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
Type *t1 = exp->e1->type->toBasetype();
Type *t2 = exp->e2->type->toBasetype();
if ((t1->ty == Tclass && exp->e2->op == TOKnull) ||
(t2->ty == Tclass && exp->e1->op == TOKnull))
{
exp->error("do not use null when comparing class types");
return setError();
}
Expression *e = exp->op_overload(sc);
if (e)
{
if (!e->type->isscalar() && e->type->equals(exp->e1->type))
{
exp->error("recursive opCmp expansion");
return setError();
}
if (e->op == TOKcall)
{
e = new CmpExp(exp->op, exp->loc, e, new IntegerExp(exp->loc, 0, Type::tint32));
e = expressionSemantic(e, sc);
}
result = e;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
bool f1 = checkNonAssignmentArrayOp(exp->e1);
bool f2 = checkNonAssignmentArrayOp(exp->e2);
if (f1 || f2)
return setError();
exp->type = Type::tbool;
// Special handling for array comparisons
t1 = exp->e1->type->toBasetype();
t2 = exp->e2->type->toBasetype();
if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) &&
(t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer))
{
Type *t1next = t1->nextOf();
Type *t2next = t2->nextOf();
if (t1next->implicitConvTo(t2next) < MATCHconst &&
t2next->implicitConvTo(t1next) < MATCHconst &&
(t1next->ty != Tvoid && t2next->ty != Tvoid))
{
exp->error("array comparison type mismatch, %s vs %s", t1next->toChars(), t2next->toChars());
return setError();
}
if ((t1->ty == Tarray || t1->ty == Tsarray) &&
(t2->ty == Tarray || t2->ty == Tsarray))
{
semanticTypeInfo(sc, t1->nextOf());
}
}
else if (t1->ty == Tstruct || t2->ty == Tstruct ||
(t1->ty == Tclass && t2->ty == Tclass))
{
if (t2->ty == Tstruct)
exp->error("need member function opCmp() for %s %s to compare", t2->toDsymbol(sc)->kind(), t2->toChars());
else
exp->error("need member function opCmp() for %s %s to compare", t1->toDsymbol(sc)->kind(), t1->toChars());
return setError();
}
else if (t1->iscomplex() || t2->iscomplex())
{
exp->error("compare not defined for complex operands");
return setError();
}
else if (t1->ty == Taarray || t2->ty == Taarray)
{
exp->error("%s is not defined for associative arrays", Token::toChars(exp->op));
return setError();
}
else if (!target.isVectorOpSupported(t1, exp->op, t2))
{
result = exp->incompatibleTypes();
return;
}
else
{
bool r1 = exp->e1->checkValue();
bool r2 = exp->e2->checkValue();
if (r1 || r2)
return setError();
}
TOK altop;
switch (exp->op)
{
// Refer rel_integral[] table
case TOKunord: altop = TOKerror; break;
case TOKlg: altop = TOKnotequal; break;
case TOKleg: altop = TOKerror; break;
case TOKule: altop = TOKle; break;
case TOKul: altop = TOKlt; break;
case TOKuge: altop = TOKge; break;
case TOKug: altop = TOKgt; break;
case TOKue: altop = TOKequal; break;
default: altop = TOKreserved; break;
}
if (altop == TOKerror &&
(t1->ty == Tarray || t1->ty == Tsarray ||
t2->ty == Tarray || t2->ty == Tsarray))
{
exp->error("`%s` is not defined for array comparisons", Token::toChars(exp->op));
return setError();
}
if (altop != TOKreserved)
{
if (!t1->isfloating())
{
if (altop == TOKerror)
{
const char *s = exp->op == TOKunord ? "false" : "true";
exp->error("floating point operator `%s` always returns %s for non-floating comparisons",
Token::toChars(exp->op), s);
}
else
{
exp->error("use `%s` for non-floating comparisons rather than floating point operator `%s`",
Token::toChars(altop), Token::toChars(exp->op));
}
}
else
{
exp->error("use std.math.isNaN to deal with NaN operands rather than floating point operator `%s`",
Token::toChars(exp->op));
}
return setError();
}
//printf("CmpExp: %s, type = %s\n", e->toChars(), e->type->toChars());
result = exp;
}
void visit(EqualExp *exp)
{
//printf("EqualExp::semantic('%s')\n", toChars());
if (exp->type)
{
result = exp;
return;
}
setNoderefOperands(exp);
if (Expression *e = binSemanticProp(exp, sc))
{
result = e;
return;
}
if (exp->e1->op == TOKtype || exp->e2->op == TOKtype)
{
result = exp->incompatibleTypes();
return;
}
{
Type *t1 = exp->e1->type;
Type *t2 = exp->e2->type;
if (t1->ty == Tenum && t2->ty == Tenum && !t1->equivalent(t2))
exp->deprecation("Comparison between different enumeration types `%s` and `%s`; If this behavior is intended consider using `std.conv.asOriginalType`",
t1->toChars(), t2->toChars());
}
/* Before checking for operator overloading, check to see if we're
* comparing the addresses of two statics. If so, we can just see
* if they are the same symbol.
*/
if (exp->e1->op == TOKaddress && exp->e2->op == TOKaddress)
{
AddrExp *ae1 = (AddrExp *)exp->e1;
AddrExp *ae2 = (AddrExp *)exp->e2;
if (ae1->e1->op == TOKvar && ae2->e1->op == TOKvar)
{
VarExp *ve1 = (VarExp *)ae1->e1;
VarExp *ve2 = (VarExp *)ae2->e1;
if (ve1->var == ve2->var)
{
// They are the same, result is 'true' for ==, 'false' for !=
result = new IntegerExp(exp->loc, (exp->op == TOKequal), Type::tbool);
return;
}
}
}
if (Expression *e = exp->op_overload(sc))
{
result = e;
return;
}
if (Expression *e = typeCombine(exp, sc))
{
result = e;
return;
}
bool f1 = checkNonAssignmentArrayOp(exp->e1);
bool f2 = checkNonAssignmentArrayOp(exp->e2);
if (f1 || f2)
return setError();
exp->type = Type::tbool;
// Special handling for array comparisons
if (!arrayTypeCompatible(exp->loc, exp->e1->type, exp->e2->type))
{
if (exp->e1->type != exp->e2->type && exp->e1->type->isfloating() && exp->e2->type->isfloating())
{
// Cast both to complex
exp->e1 = exp->e1->castTo(sc, Type::tcomplex80);
exp->e2 = exp->e2->castTo(sc, Type::tcomplex80);
}
}
if (exp->e1->type->toBasetype()->ty == Taarray)
semanticTypeInfo(sc, exp->e1->type->toBasetype());
Type *t1 = exp->e1->type->toBasetype();
Type *t2 = exp->e2->type->toBasetype();
if (!target.isVectorOpSupported(t1, exp->op, t2))
{
result = exp->incompatibleTypes();
return;
}
result = exp;
}
void visit(IdentityExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
setNoderefOperands(exp);
if (Expression *ex = binSemanticProp(exp, sc))
{
result = ex;
return;
}
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
bool f1 = checkNonAssignmentArrayOp(exp->e1);
bool f2 = checkNonAssignmentArrayOp(exp->e2);
if (f1 || f2)
return setError();
if (exp->e1->op == TOKtype || exp->e2->op == TOKtype)
{
result = exp->incompatibleTypes();
return;
}
exp->type = Type::tbool;
if (exp->e1->type != exp->e2->type && exp->e1->type->isfloating() && exp->e2->type->isfloating())
{
// Cast both to complex
exp->e1 = exp->e1->castTo(sc, Type::tcomplex80);
exp->e2 = exp->e2->castTo(sc, Type::tcomplex80);
}
Type *tb1 = exp->e1->type->toBasetype();
Type *tb2 = exp->e2->type->toBasetype();
if (!target.isVectorOpSupported(tb1, exp->op, tb2))
{
result = exp->incompatibleTypes();
return;
}
result = exp;
}
void visit(CondExp *exp)
{
if (exp->type)
{
result = exp;
return;
}
if (exp->econd->op == TOKdotid)
((DotIdExp *)exp->econd)->noderef = true;
Expression *ec = expressionSemantic(exp->econd, sc);
ec = resolveProperties(sc, ec);
ec = ec->toBoolean(sc);
unsigned cs0 = sc->callSuper;
unsigned *fi0 = sc->saveFieldInit();
Expression *e1x = expressionSemantic(exp->e1, sc);
e1x = resolveProperties(sc, e1x);
unsigned cs1 = sc->callSuper;
unsigned *fi1 = sc->fieldinit;
sc->callSuper = cs0;
sc->fieldinit = fi0;
Expression *e2x = expressionSemantic(exp->e2, sc);
e2x = resolveProperties(sc, e2x);
sc->mergeCallSuper(exp->loc, cs1);
sc->mergeFieldInit(exp->loc, fi1);
if (ec->op == TOKerror)
{
result = ec;
return;
}
if (ec->type == Type::terror)
return setError();
exp->econd = ec;
if (e1x->op == TOKerror)
{
result = e1x;
return;
}
if (e1x->type == Type::terror)
return setError();
exp->e1 = e1x;
if (e2x->op == TOKerror)
{
result = e2x;
return;
}
if (e2x->type == Type::terror)
return setError();
exp->e2 = e2x;
bool f0 = checkNonAssignmentArrayOp(exp->econd);
bool f1 = checkNonAssignmentArrayOp(exp->e1);
bool f2 = checkNonAssignmentArrayOp(exp->e2);
if (f0 || f1 || f2)
return setError();
Type *t1 = exp->e1->type;
Type *t2 = exp->e2->type;
if (t1->ty == Tnoreturn)
{
exp->type = t2;
}
else if (t2->ty == Tnoreturn)
{
exp->type = t1;
}
// If either operand is void the result is void, we have to cast both
// the expression to void so that we explicitly discard the expression
// value if any (bugzilla 16598)
else if (t1->ty == Tvoid || t2->ty == Tvoid)
{
exp->type = Type::tvoid;
exp->e1 = exp->e1->castTo(sc, exp->type);
exp->e2 = exp->e2->castTo(sc, exp->type);
}
else if (t1 == t2)
exp->type = t1;
else
{
if (Expression *ex = typeCombine(exp, sc))
{
result = ex;
return;
}
switch (exp->e1->type->toBasetype()->ty)
{
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
exp->e2 = exp->e2->castTo(sc, exp->e1->type);
break;
}
switch (exp->e2->type->toBasetype()->ty)
{
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
exp->e1 = exp->e1->castTo(sc, exp->e2->type);
break;
}
if (exp->type->toBasetype()->ty == Tarray)
{
exp->e1 = exp->e1->castTo(sc, exp->type);
exp->e2 = exp->e2->castTo(sc, exp->type);
}
}
exp->type = exp->type->merge2();
/* Bugzilla 14696: If either e1 or e2 contain temporaries which need dtor,
* make them conditional.
* Rewrite:
* cond ? (__tmp1 = ..., __tmp1) : (__tmp2 = ..., __tmp2)
* to:
* (auto __cond = cond) ? (... __tmp1) : (... __tmp2)
* and replace edtors of __tmp1 and __tmp2 with:
* __tmp1->edtor --> __cond && __tmp1.dtor()
* __tmp2->edtor --> __cond || __tmp2.dtor()
*/
exp->hookDtors(sc);
result = exp;
}
void visit(FileInitExp *e)
{
//printf("FileInitExp::semantic()\n");
e->type = Type::tstring;
result = e;
}
void visit(LineInitExp *e)
{
e->type = Type::tint32;
result = e;
}
void visit(ModuleInitExp *e)
{
//printf("ModuleInitExp::semantic()\n");
e->type = Type::tstring;
result = e;
}
void visit(FuncInitExp *e)
{
//printf("FuncInitExp::semantic()\n");
e->type = Type::tstring;
if (sc->func)
{
result = e->resolveLoc(Loc(), sc);
return;
}
result = e;
}
void visit(PrettyFuncInitExp *e)
{
//printf("PrettyFuncInitExp::semantic()\n");
e->type = Type::tstring;
if (sc->func)
{
result = e->resolveLoc(Loc(), sc);
return;
}
result = e;
}
};
/**********************************
* Try to run semantic routines.
* If they fail, return NULL.
*/
Expression *trySemantic(Expression *exp, Scope* sc)
{
//printf("+trySemantic(%s)\n", toChars());
unsigned errors = global.startGagging();
Expression *e = expressionSemantic(exp, sc);
if (global.endGagging(errors))
{
e = NULL;
}
//printf("-trySemantic(%s)\n", toChars());
return e;
}
/**************************
* Helper function for easy error propagation.
* If error occurs, returns ErrorExp. Otherwise returns NULL.
*/
Expression *unaSemantic(UnaExp *e, Scope *sc)
{
Expression *e1x = expressionSemantic(e->e1, sc);
if (e1x->op == TOKerror)
return e1x;
e->e1 = e1x;
return NULL;
}
/**************************
* Helper function for easy error propagation.
* If error occurs, returns ErrorExp. Otherwise returns NULL.
*/
Expression *binSemantic(BinExp *e, Scope *sc)
{
Expression *e1x = expressionSemantic(e->e1, sc);
Expression *e2x = expressionSemantic(e->e2, sc);
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
if (e1x->op == TOKtype)
e1x = resolveAliasThis(sc, e1x);
if (e2x->op == TOKtype)
e2x = resolveAliasThis(sc, e2x);
if (e1x->op == TOKerror)
return e1x;
if (e2x->op == TOKerror)
return e2x;
e->e1 = e1x;
e->e2 = e2x;
return NULL;
}
Expression *binSemanticProp(BinExp *e, Scope *sc)
{
if (Expression *ex = binSemantic(e, sc))
return ex;
Expression *e1x = resolveProperties(sc, e->e1);
Expression *e2x = resolveProperties(sc, e->e2);
if (e1x->op == TOKerror)
return e1x;
if (e2x->op == TOKerror)
return e2x;
e->e1 = e1x;
e->e2 = e2x;
return NULL;
}
// entrypoint for semantic ExpressionSemanticVisitor
Expression *expressionSemantic(Expression *e, Scope *sc)
{
ExpressionSemanticVisitor v = ExpressionSemanticVisitor(sc);
e->accept(&v);
return v.result;
}
Expression *semanticX(DotIdExp *exp, Scope *sc)
{
//printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars());
if (Expression *ex = unaSemantic(exp, sc))
return ex;
if (exp->ident == Id::_mangleof)
{
// symbol.mangleof
Dsymbol *ds;
switch (exp->e1->op)
{
case TOKscope:
ds = ((ScopeExp *)exp->e1)->sds;
goto L1;
case TOKvar:
ds = ((VarExp *)exp->e1)->var;
goto L1;
case TOKdotvar:
ds = ((DotVarExp *)exp->e1)->var;
goto L1;
case TOKoverloadset:
ds = ((OverExp *)exp->e1)->vars;
goto L1;
case TOKtemplate:
{
TemplateExp *te = (TemplateExp *)exp->e1;
ds = te->fd ? (Dsymbol *)te->fd : te->td;
}
L1:
{
assert(ds);
if (FuncDeclaration *f = ds->isFuncDeclaration())
{
if (f->checkForwardRef(exp->loc))
return new ErrorExp();
}
OutBuffer buf;
mangleToBuffer(ds, &buf);
const char *s = buf.extractChars();
Expression *e = new StringExp(exp->loc, const_cast<char*>(s), strlen(s));
e = expressionSemantic(e, sc);
return e;
}
default:
break;
}
}
if (exp->e1->op == TOKvar && exp->e1->type->toBasetype()->ty == Tsarray && exp->ident == Id::length)
{
// bypass checkPurity
return exp->e1->type->dotExp(sc, exp->e1, exp->ident, exp->noderef ? 2 : 0);
}
if (exp->e1->op == TOKdot)
{
}
else
{
exp->e1 = resolvePropertiesX(sc, exp->e1);
}
if (exp->e1->op == TOKtuple && exp->ident == Id::offsetof)
{
/* 'distribute' the .offsetof to each of the tuple elements.
*/
TupleExp *te = (TupleExp *)exp->e1;
Expressions *exps = new Expressions();
exps->setDim(te->exps->length);
for (size_t i = 0; i < exps->length; i++)
{
Expression *e = (*te->exps)[i];
e = expressionSemantic(e, sc);
e = new DotIdExp(e->loc, e, Id::offsetof);
(*exps)[i] = e;
}
// Don't evaluate te->e0 in runtime
Expression *e = new TupleExp(exp->loc, NULL, exps);
e = expressionSemantic(e, sc);
return e;
}
if (exp->e1->op == TOKtuple && exp->ident == Id::length)
{
TupleExp *te = (TupleExp *)exp->e1;
// Don't evaluate te->e0 in runtime
Expression *e = new IntegerExp(exp->loc, te->exps->length, Type::tsize_t);
return e;
}
// Bugzilla 14416: Template has no built-in properties except for 'stringof'.
if ((exp->e1->op == TOKdottd || exp->e1->op == TOKtemplate) && exp->ident != Id::stringof)
{
exp->error("template %s does not have property `%s`", exp->e1->toChars(), exp->ident->toChars());
return new ErrorExp();
}
if (!exp->e1->type)
{
exp->error("expression %s does not have property `%s`", exp->e1->toChars(), exp->ident->toChars());
return new ErrorExp();
}
return exp;
}
// Resolve e1.ident without seeing UFCS.
// If flag == 1, stop "not a property" error and return NULL.
Expression *semanticY(DotIdExp *exp, Scope *sc, int flag)
{
//printf("DotIdExp::semanticY(this = %p, '%s')\n", this, toChars());
//{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
/* Special case: rewrite this.id and super.id
* to be classtype.id and baseclasstype.id
* if we have no this pointer.
*/
if ((exp->e1->op == TOKthis || exp->e1->op == TOKsuper) && !hasThis(sc))
{
if (AggregateDeclaration *ad = sc->getStructClassScope())
{
if (exp->e1->op == TOKthis)
{
exp->e1 = new TypeExp(exp->e1->loc, ad->type);
}
else
{
ClassDeclaration *cd = ad->isClassDeclaration();
if (cd && cd->baseClass)
exp->e1 = new TypeExp(exp->e1->loc, cd->baseClass->type);
}
}
}
Expression *e = semanticX(exp, sc);
if (e != exp)
return e;
Expression *eleft;
Expression *eright;
if (exp->e1->op == TOKdot)
{
DotExp *de = (DotExp *)exp->e1;
eleft = de->e1;
eright = de->e2;
}
else
{
eleft = NULL;
eright = exp->e1;
}
Type *t1b = exp->e1->type->toBasetype();
if (eright->op == TOKscope) // also used for template alias's
{
ScopeExp *ie = (ScopeExp *)eright;
int flags = SearchLocalsOnly;
/* Disable access to another module's private imports.
* The check for 'is sds our current module' is because
* the current module should have access to its own imports.
*/
if (ie->sds->isModule() && ie->sds != sc->_module)
flags |= IgnorePrivateImports;
if (sc->flags & SCOPEignoresymbolvisibility)
flags |= IgnoreSymbolVisibility;
Dsymbol *s = ie->sds->search(exp->loc, exp->ident, flags);
/* Check for visibility before resolving aliases because public
* aliases to private symbols are public.
*/
if (s && !(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc->_module, s))
{
s = NULL;
}
if (s)
{
Package *p = s->isPackage();
if (p && checkAccess(sc, p))
{
s = NULL;
}
}
if (s)
{
// if 's' is a tuple variable, the tuple is returned.
s = s->toAlias();
exp->checkDeprecated(sc, s);
exp->checkDisabled(sc, s);
EnumMember *em = s->isEnumMember();
if (em)
{
return em->getVarExp(exp->loc, sc);
}
VarDeclaration *v = s->isVarDeclaration();
if (v)
{
//printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
if (!v->type ||
(!v->type->deco && v->inuse))
{
if (v->inuse)
exp->error("circular reference to %s `%s`", v->kind(), v->toPrettyChars());
else
exp->error("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 && !exp->wantsym)
{
/* Normally, the replacement of a symbol with its initializer is supposed to be in semantic2().
* Introduced by https://github.com/dlang/dmd/pull/5588 which should probably
* be reverted. `wantsym` is the hack to work around the problem.
*/
if (v->inuse)
{
::error(exp->loc, "circular initialization of %s `%s`", v->kind(), v->toPrettyChars());
return new ErrorExp();
}
e = v->expandInitializer(exp->loc);
v->inuse++;
e = expressionSemantic(e, sc);
v->inuse--;
return e;
}
if (v->needThis())
{
if (!eleft)
eleft = new ThisExp(exp->loc);
e = new DotVarExp(exp->loc, eleft, v);
e = expressionSemantic(e, sc);
}
else
{
e = new VarExp(exp->loc, v);
if (eleft)
{ e = new CommaExp(exp->loc, eleft, e);
e->type = v->type;
}
}
e = e->deref();
return expressionSemantic(e, sc);
}
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
//printf("it's a function\n");
if (!f->functionSemantic())
return new ErrorExp();
if (f->needThis())
{
if (!eleft)
eleft = new ThisExp(exp->loc);
e = new DotVarExp(exp->loc, eleft, f, true);
e = expressionSemantic(e, sc);
}
else
{
e = new VarExp(exp->loc, f, true);
if (eleft)
{ e = new CommaExp(exp->loc, eleft, e);
e->type = f->type;
}
}
return e;
}
if (TemplateDeclaration *td = s->isTemplateDeclaration())
{
if (eleft)
e = new DotTemplateExp(exp->loc, eleft, td);
else
e = new TemplateExp(exp->loc, td);
e = expressionSemantic(e, sc);
return e;
}
if (OverDeclaration *od = s->isOverDeclaration())
{
e = new VarExp(exp->loc, od, true);
if (eleft)
{
e = new CommaExp(exp->loc, eleft, e);
e->type = Type::tvoid; // ambiguous type?
}
return e;
}
OverloadSet *o = s->isOverloadSet();
if (o)
{ //printf("'%s' is an overload set\n", o->toChars());
return new OverExp(exp->loc, o);
}
if (Type *t = s->getType())
{
return expressionSemantic(new TypeExp(exp->loc, t), sc);
}
TupleDeclaration *tup = s->isTupleDeclaration();
if (tup)
{
if (eleft)
{
e = new DotVarExp(exp->loc, eleft, tup);
e = expressionSemantic(e, sc);
return e;
}
e = new TupleExp(exp->loc, tup);
e = expressionSemantic(e, sc);
return e;
}
ScopeDsymbol *sds = s->isScopeDsymbol();
if (sds)
{
//printf("it's a ScopeDsymbol %s\n", exp->ident->toChars());
e = new ScopeExp(exp->loc, sds);
e = expressionSemantic(e, sc);
if (eleft)
e = new DotExp(exp->loc, eleft, e);
return e;
}
Import *imp = s->isImport();
if (imp)
{
ie = new ScopeExp(exp->loc, imp->pkg);
return expressionSemantic(ie, sc);
}
// BUG: handle other cases like in IdentifierExp::semantic()
assert(0);
}
else if (exp->ident == Id::stringof)
{
const char *p = ie->toChars();
e = new StringExp(exp->loc, const_cast<char *>(p), strlen(p));
e = expressionSemantic(e, sc);
return e;
}
if (ie->sds->isPackage() ||
ie->sds->isImport() ||
ie->sds->isModule())
{
flag = 0;
}
if (flag)
return NULL;
s = ie->sds->search_correct(exp->ident);
if (s)
{
if (s->isPackage())
exp->error("undefined identifier `%s` in %s `%s`, perhaps add `static import %s;`",
exp->ident->toChars(), ie->sds->kind(), ie->sds->toPrettyChars(), s->toPrettyChars());
else
exp->error("undefined identifier `%s` in %s `%s`, did you mean %s `%s`?",
exp->ident->toChars(), ie->sds->kind(), ie->sds->toPrettyChars(), s->kind(), s->toChars());
}
else
exp->error("undefined identifier `%s` in %s `%s`",
exp->ident->toChars(), ie->sds->kind(), ie->sds->toPrettyChars());
return new ErrorExp();
}
else if (t1b->ty == Tpointer && exp->e1->type->ty != Tenum &&
exp->ident != Id::_init && exp->ident != Id::__sizeof &&
exp->ident != Id::__xalignof && exp->ident != Id::offsetof &&
exp->ident != Id::_mangleof && exp->ident != Id::stringof)
{
Type *t1bn = t1b->nextOf();
if (flag)
{
AggregateDeclaration *ad = isAggregate(t1bn);
if (ad && !ad->members) // Bugzilla 11312
return NULL;
}
/* Rewrite:
* p.ident
* as:
* (*p).ident
*/
if (flag && t1bn->ty == Tvoid)
return NULL;
e = new PtrExp(exp->loc, exp->e1);
e = expressionSemantic(e, sc);
return e->type->dotExp(sc, e, exp->ident, flag | (exp->noderef ? 2 : 0));
}
else
{
if (exp->e1->op == TOKtype || exp->e1->op == TOKtemplate)
flag = 0;
e = exp->e1->type->dotExp(sc, exp->e1, exp->ident, flag | (exp->noderef ? 2 : 0));
if (e)
e = expressionSemantic(e, sc);
return e;
}
}
// Resolve e1.ident!tiargs without seeing UFCS.
// If flag == 1, stop "not a property" error and return NULL.
Expression *semanticY(DotTemplateInstanceExp *exp, Scope *sc, int flag)
{
DotIdExp *die = new DotIdExp(exp->loc, exp->e1, exp->ti->name);
Expression *e = semanticX(die, sc);
if (e == die)
{
exp->e1 = die->e1; // take back
Type *t1b = exp->e1->type->toBasetype();
if (t1b->ty == Tarray || t1b->ty == Tsarray || t1b->ty == Taarray ||
t1b->ty == Tnull || (t1b->isTypeBasic() && t1b->ty != Tvoid))
{
/* No built-in type has templatized properties, so do shortcut.
* It is necessary in: 1024.max!"a < b"
*/
if (flag)
return NULL;
}
e = semanticY(die, sc, flag);
if (flag && e && isDotOpDispatch(e))
{
/* opDispatch!tiargs would be a function template that needs IFTI,
* so it's not a template
*/
e = NULL; /* fall down to UFCS */
}
if (flag && !e)
return NULL;
}
assert(e);
if (e->op == TOKerror)
return e;
if (e->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)e;
if (FuncDeclaration *fd = dve->var->isFuncDeclaration())
{
TemplateDeclaration *td = fd->findTemplateDeclRoot();
if (td)
{
e = new DotTemplateExp(dve->loc, dve->e1, td);
e = expressionSemantic(e, sc);
}
}
else if (dve->var->isOverDeclaration())
{
exp->e1 = dve->e1; // pull semantic() result
if (!exp->findTempDecl(sc))
goto Lerr;
if (exp->ti->needsTypeInference(sc))
return exp;
dsymbolSemantic(exp->ti, sc);
if (!exp->ti->inst || exp->ti->errors) // if template failed to expand
return new ErrorExp();
Dsymbol *s = exp->ti->toAlias();
Declaration *v = s->isDeclaration();
if (v)
{
if (v->type && !v->type->deco)
v->type = typeSemantic(v->type, v->loc, sc);
e = new DotVarExp(exp->loc, exp->e1, v);
e = expressionSemantic(e, sc);
return e;
}
e = new ScopeExp(exp->loc, exp->ti);
e = new DotExp(exp->loc, exp->e1, e);
e = expressionSemantic(e, sc);
return e;
}
}
else if (e->op == TOKvar)
{
VarExp *ve = (VarExp *)e;
if (FuncDeclaration *fd = ve->var->isFuncDeclaration())
{
TemplateDeclaration *td = fd->findTemplateDeclRoot();
if (td)
{
e = new TemplateExp(ve->loc, td);
e = expressionSemantic(e, sc);
}
}
else if (OverDeclaration *od = ve->var->isOverDeclaration())
{
exp->ti->tempdecl = od;
e = new ScopeExp(exp->loc, exp->ti);
e = expressionSemantic(e, sc);
return e;
}
}
if (e->op == TOKdottd)
{
DotTemplateExp *dte = (DotTemplateExp *)e;
exp->e1 = dte->e1; // pull semantic() result
exp->ti->tempdecl = dte->td;
if (!exp->ti->semanticTiargs(sc))
return new ErrorExp();
if (exp->ti->needsTypeInference(sc))
return exp;
dsymbolSemantic(exp->ti, sc);
if (!exp->ti->inst || exp->ti->errors) // if template failed to expand
return new ErrorExp();
Dsymbol *s = exp->ti->toAlias();
Declaration *v = s->isDeclaration();
if (v && (v->isFuncDeclaration() || v->isVarDeclaration()))
{
e = new DotVarExp(exp->loc, exp->e1, v);
e = expressionSemantic(e, sc);
return e;
}
e = new ScopeExp(exp->loc, exp->ti);
e = new DotExp(exp->loc, exp->e1, e);
e = expressionSemantic(e, sc);
return e;
}
else if (e->op == TOKtemplate)
{
exp->ti->tempdecl = ((TemplateExp *)e)->td;
e = new ScopeExp(exp->loc, exp->ti);
e = expressionSemantic(e, sc);
return e;
}
else if (e->op == TOKdot)
{
DotExp *de = (DotExp *)e;
if (de->e2->op == TOKoverloadset)
{
if (!exp->findTempDecl(sc) ||
!exp->ti->semanticTiargs(sc))
{
return new ErrorExp();
}
if (exp->ti->needsTypeInference(sc))
return exp;
dsymbolSemantic(exp->ti, sc);
if (!exp->ti->inst || exp->ti->errors) // if template failed to expand
return new ErrorExp();
Dsymbol *s = exp->ti->toAlias();
Declaration *v = s->isDeclaration();
if (v)
{
if (v->type && !v->type->deco)
v->type = typeSemantic(v->type, v->loc, sc);
e = new DotVarExp(exp->loc, exp->e1, v);
e = expressionSemantic(e, sc);
return e;
}
e = new ScopeExp(exp->loc, exp->ti);
e = new DotExp(exp->loc, exp->e1, e);
e = expressionSemantic(e, sc);
return e;
}
}
else if (e->op == TOKoverloadset)
{
OverExp *oe = (OverExp *)e;
exp->ti->tempdecl = oe->vars;
e = new ScopeExp(exp->loc, exp->ti);
e = expressionSemantic(e, sc);
return e;
}
Lerr:
e->error("%s isn't a template", e->toChars());
return new ErrorExp();
}