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);