Files
ldc/dmd/expression.c
David Nadlinger d7eead617e Merged DMD commit c7f94c6b66d3b4233ea01dd3dd9f0028b7118870:
5349 ICE(toir.c): nested class in static member function

When checking if a nested class can reach a parent class, it should not be
possible to jump through static functions. Static member functions were just
being silently ignored, which meant nonsense was passed to the backend.
(Didn't always cause an ICE, sometimes generates nonsense code).
2011-04-23 18:28:36 +02:00

10215 lines
260 KiB
C

// Compiler implementation of the D programming language
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <assert.h>
#if _MSC_VER
#include <complex>
#else
#endif
#if _WIN32 && __DMC__
extern "C" char * __cdecl __locale_decpoint;
#endif
#if __MINGW32__
#ifndef isnan
#define isnan _isnan
#endif
#endif
#ifdef __APPLE__
#ifndef isnan
int isnan(double);
#endif
#endif
#include "rmem.h"
#include "port.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"
Expression *expandVar(int result, VarDeclaration *v);
#define LOGSEMANTIC 0
/*************************************************************
* Given var, we need to get the
* right 'this' pointer if var is in an outer class, but our
* existing 'this' pointer is in an inner class.
* Input:
* e1 existing 'this'
* ad struct or class we need the correct 'this' for
* var the specific member of ad we're accessing
*/
Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad,
Expression *e1, Declaration *var)
{
//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;
// Do not call checkNestedRef()
//e1 = e1->semantic(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++;
// LDC seems dmd misses it sometimes here :/
f->vthis->nestedref = 1;
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;
if (n > 1)
e1 = e1->semantic(sc);
}
else
e1 = e1->semantic(sc);
goto L1;
}
/* Can't find a path from e1 to ad
*/
e1->error("this for %s needs to be type %s not type %s",
var->toChars(), ad->toChars(), t->toChars());
e1 = new ErrorExp();
}
}
return e1;
}
/*****************************************
* Determine if 'this' is available.
* If it is, return the FuncDeclaration that has it.
*/
FuncDeclaration *hasThis(Scope *sc)
{ FuncDeclaration *fd;
FuncDeclaration *fdthis;
//printf("hasThis()\n");
fdthis = sc->parent->isFuncDeclaration();
//printf("fdthis = %p, '%s'\n", fdthis, fdthis ? fdthis->toChars() : "");
// Go upwards until we find the enclosing member function
fd = fdthis;
while (1)
{
if (!fd)
{
goto Lno;
}
if (!fd->isNested())
break;
Dsymbol *parent = fd->parent;
while (parent)
{
TemplateInstance *ti = parent->isTemplateInstance();
if (ti)
parent = ti->parent;
else
break;
}
fd = fd->parent->isFuncDeclaration();
}
if (!fd->isThis())
{ //printf("test '%s'\n", fd->toChars());
goto Lno;
}
assert(fd->vthis);
return fd;
Lno:
return NULL; // don't have 'this' available
}
/***************************************
* Pull out any properties.
*/
Expression *resolveProperties(Scope *sc, Expression *e)
{
//printf("resolveProperties(%s)\n", e->toChars());
if (e->type)
{
Type *t = e->type->toBasetype();
if (t->ty == Tfunction /*|| e->op == TOKoverloadset*/)
{
e = new CallExp(e->loc, e);
e = e->semantic(sc);
}
/* Look for e being a lazy parameter; rewrite as delegate call
*/
else if (e->op == TOKvar)
{ VarExp *ve = (VarExp *)e;
if (ve->var->storage_class & STClazy)
{
e = new CallExp(e->loc, e);
e = e->semantic(sc);
}
}
else if (e->op == TOKdotexp)
{
e->error("expression has no value");
return new ErrorExp();
}
}
else if (e->op == TOKdottd)
{
e = new CallExp(e->loc, e);
e = e->semantic(sc);
}
return e;
}
/******************************
* Perform semantic() on an array of Expressions.
*/
Expressions *arrayExpressionSemantic(Expressions *exps, Scope *sc)
{
if (exps)
{
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
if (e)
{ e = e->semantic(sc);
exps->data[i] = (void *)e;
}
}
}
return exps;
}
/******************************
* Perform canThrow() on an array of Expressions.
*/
#if DMDV2
int arrayExpressionCanThrow(Expressions *exps)
{
if (exps)
{
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
if (e && e->canThrow())
return 1;
}
}
return 0;
}
#endif
/****************************************
* Expand tuples.
*/
void expandTuples(Expressions *exps)
{
//printf("expandTuples()\n");
if (exps)
{
for (size_t i = 0; i < exps->dim; i++)
{ Expression *arg = (Expression *)exps->data[i];
if (!arg)
continue;
// Look for tuple with 0 members
if (arg->op == TOKtype)
{ TypeExp *e = (TypeExp *)arg;
if (e->type->toBasetype()->ty == Ttuple)
{ TypeTuple *tt = (TypeTuple *)e->type->toBasetype();
if (!tt->arguments || tt->arguments->dim == 0)
{
exps->remove(i);
if (i == exps->dim)
return;
i--;
continue;
}
}
}
// Inline expand all the tuples
while (arg->op == TOKtuple)
{ TupleExp *te = (TupleExp *)arg;
exps->remove(i); // remove arg
exps->insert(i, te->exps); // replace with tuple contents
if (i == exps->dim)
return; // empty tuple, no more arguments
arg = (Expression *)exps->data[i];
}
}
}
}
Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt)
{
#if DMDV1
/* The first element sets the type
*/
Type *t0 = NULL;
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
if (!e->type)
{ error("%s has no value", e->toChars());
e = new ErrorExp();
}
e = resolveProperties(sc, e);
if (!t0)
t0 = e->type;
else
e = e->implicitCastTo(sc, t0);
exps->data[i] = (void *)e;
}
if (!t0)
t0 = Type::tvoid;
if (pt)
*pt = t0;
// Eventually, we want to make this copy-on-write
return exps;
#endif
#if DMDV2
/* The type is determined by applying ?: to each pair.
*/
/* 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(0, &integerexp, NULL, NULL);
Type *t0 = NULL;
Expression *e0;
int j0;
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
e = resolveProperties(sc, e);
if (!e->type)
{ error("%s has no value", e->toChars());
e = new ErrorExp();
}
if (t0)
{ if (t0 != 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.semantic(sc);
exps->data[j0] = (void *)condexp.e1;
e = condexp.e2;
j0 = i;
e0 = e;
t0 = e0->type;
}
}
else
{ j0 = i;
e0 = e;
t0 = e->type;
}
exps->data[i] = (void *)e;
}
if (t0)
{
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
e = e->implicitCastTo(sc, t0);
exps->data[i] = (void *)e;
}
}
else
t0 = Type::tvoid; // [] is typed as void[]
if (pt)
*pt = t0;
// Eventually, we want to make this copy-on-write
return exps;
#endif
}
/****************************************
* Preprocess arguments to function.
*/
void preFunctionParameters(Loc loc, Scope *sc, Expressions *exps)
{
if (exps)
{
expandTuples(exps);
for (size_t i = 0; i < exps->dim; i++)
{ Expression *arg = (Expression *)exps->data[i];
if (!arg->type)
{
#ifdef DEBUG
if (!global.gag)
printf("1: \n");
#endif
arg->error("%s is not an expression", arg->toChars());
arg = new ErrorExp();
}
arg = resolveProperties(sc, arg);
exps->data[i] = (void *) arg;
//arg->rvalue();
#if 0
if (arg->type->ty == Tfunction)
{
arg = new AddrExp(arg->loc, arg);
arg = arg->semantic(sc);
exps->data[i] = (void *) arg;
}
#endif
}
}
}
/*********************************************
* Call copy constructor for struct value argument.
*/
#if DMDV2
Expression *callCpCtor(Loc loc, Scope *sc, Expression *e)
{
Type *tb = e->type->toBasetype();
assert(tb->ty == Tstruct);
StructDeclaration *sd = ((TypeStruct *)tb)->sym;
if (sd->cpctor)
{
/* Create a variable tmp, and replace the argument e with:
* (tmp = e),tmp
* and let AssignExp() handle the construction.
* This is not the most efficent, ideally tmp would be constructed
* directly onto the stack.
*/
Identifier *idtmp = Lexer::uniqueId("__tmp");
VarDeclaration *tmp = new VarDeclaration(loc, tb, idtmp, new ExpInitializer(0, e));
tmp->storage_class |= STCctfe;
Expression *ae = new DeclarationExp(loc, tmp);
e = new CommaExp(loc, ae, new VarExp(loc, tmp));
e = e->semantic(sc);
}
return e;
}
#endif
/****************************************
* 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
*/
void functionParameters(Loc loc, Scope *sc, TypeFunction *tf, Expressions *arguments)
{
//printf("functionParameters()\n");
assert(arguments);
size_t nargs = arguments ? arguments->dim : 0;
size_t nparams = Parameter::dim(tf->parameters);
if (nargs > nparams && tf->varargs == 0)
error(loc, "expected %zu arguments, not %zu for non-variadic function type %s", nparams, nargs, tf->toChars());
unsigned n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams)
int done = 0;
for (size_t i = 0; i < n; i++)
{
Expression *arg;
if (i < nargs)
arg = (Expression *)arguments->data[i];
else
arg = NULL;
Type *tb;
if (i < nparams)
{
Parameter *p = Parameter::getNth(tf->parameters, i);
if (!arg)
{
if (!p->defaultArg)
{
if (tf->varargs == 2 && i + 1 == nparams)
goto L2;
error(loc, "expected %zu function arguments, not %zu", nparams, nargs);
return;
}
arg = p->defaultArg;
arg = arg->inlineCopy(sc);
#if DMDV2
arg = arg->resolveLoc(loc, sc); // __FILE__ and __LINE__
#endif
arguments->push(arg);
nargs++;
}
if (tf->varargs == 2 && i + 1 == nparams)
{
//printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars());
if (arg->implicitConvTo(p->type))
{
if (nargs != nparams)
{ error(loc, "expected %zu function arguments, not %zu", nparams, nargs);
return;
}
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
#ifdef IN_GCC
/* GCC 4.0 does not like zero length arrays used like
this; pass a null array value instead. Could also
just make a one-element array. */
if (nargs - i == 0)
{
arg = new NullExp(loc);
break;
}
#endif
Identifier *id = Lexer::uniqueId("__arrayArg");
Type *t = new TypeSArray(((TypeArray *)tb)->next, new IntegerExp(nargs - i));
t = t->semantic(loc, sc);
VarDeclaration *v = new VarDeclaration(loc, t, id, new VoidInitializer(loc));
v->storage_class |= STCctfe;
v->semantic(sc);
v->parent = sc->parent;
//sc->insert(v);
Expression *c = new DeclarationExp(0, v);
c->type = v->type;
for (size_t u = i; u < nargs; u++)
{ Expression *a = (Expression *)arguments->data[u];
if (tret && !((TypeArray *)tb)->next->equals(a->type))
a = a->toDelegate(sc, tret);
Expression *e = new VarExp(loc, v);
e = new IndexExp(loc, e, new IntegerExp(u + 1 - nparams));
AssignExp *ae = new AssignExp(loc, e, a);
#if DMDV2
ae->op = TOKconstruct;
#endif
if (c)
c = new CommaExp(loc, c, ae);
else
c = ae;
}
arg = new VarExp(loc, v);
if (c)
arg = new CommaExp(loc, c, arg);
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->data[u - i] = arguments->data[u];
arg = new NewExp(loc, NULL, NULL, p->type, args);
break;
}
default:
if (!arg)
{ error(loc, "not enough arguments");
return;
}
break;
}
arg = arg->semantic(sc);
//printf("\targ = '%s'\n", arg->toChars());
arguments->setDim(i + 1);
done = 1;
}
L1:
if (!(p->storageClass & STClazy && p->type->ty == Tvoid))
{
if (p->type != arg->type)
{
//printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars());
if (arg->op == TOKtype)
arg->error("cannot pass type %s as function argument", arg->toChars());
arg = arg->implicitCastTo(sc, p->type);
arg = arg->optimize(WANTvalue);
}
}
if (p->storageClass & (STCout | STCref))
{
// BUG: should check that argument to ref is type 'invariant'
// BUG: assignments to ref should also be type 'invariant'
arg = arg->modifiableLvalue(sc, arg);
//if (arg->op == TOKslice)
//arg->error("cannot modify slice %s", arg->toChars());
}
// LDC we don't want this!
#if !IN_LLVM
// Convert static arrays to pointers
tb = arg->type->toBasetype();
if (tb->ty == Tsarray)
{
arg = arg->checkToPointer();
}
#endif
#if DMDV2
if (tb->ty == Tstruct && !(p->storageClass & (STCref | STCout)))
{
arg = callCpCtor(loc, sc, arg);
}
#endif
// Convert lazy argument to a delegate
if (p->storageClass & STClazy)
{
arg = arg->toDelegate(sc, p->type);
}
#if DMDV2
/* Look for arguments that cannot 'escape' from the called
* function.
*/
if (!tf->parameterEscapes(p))
{
/* Function literals can only appear once, so if this
* appearance was scoped, there cannot be any others.
*/
if (arg->op == TOKfunction)
{ FuncExp *fe = (FuncExp *)arg;
fe->fd->tookAddressOf = 0;
}
/* 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.
*/
else if (arg->op == TOKdelegate)
{ DelegateExp *de = (DelegateExp *)arg;
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);
}
}
}
}
#endif
}
else
{
// If not D linkage, do promotions
// LDC: don't do promotions on intrinsics
if (tf->linkage != LINKd && tf->linkage != LINKintrinsic)
{
// Promote bytes, words, etc., to ints
arg = arg->integralPromotions(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;
}
}
// Convert static arrays to dynamic arrays
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 DMDV2
if (tb->ty == Tstruct)
{
arg = callCpCtor(loc, sc, arg);
}
#endif
// Give error for overloaded function addresses
#if DMDV2
if (arg->op == TOKsymoff)
{ SymOffExp *se = (SymOffExp *)arg;
if (
se->hasOverloads &&
!se->var->isFuncDeclaration()->isUnique())
arg->error("function %s is overloaded", arg->toChars());
}
#endif
arg->rvalue();
}
arg = arg->optimize(WANTvalue);
arguments->data[i] = (void *) arg;
if (done)
break;
}
#if !IN_LLVM
// If D linkage and variadic, add _arguments[] as first argument
if (tf->linkage == LINKd && tf->varargs == 1)
{
assert(arguments->dim >= nparams);
Expression *e = createTypeInfoArray(sc, (Expression **)&arguments->data[nparams],
arguments->dim - nparams);
arguments->insert(0, e);
}
#endif
}
/**************************************************
* Write expression out to buf, but wrap it
* in ( ) if its precedence is less than pr.
*/
void expToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e, enum PREC pr)
{
#ifdef DEBUG
if (precedence[e->op] == PREC_zero)
printf("precedence not defined for token '%s'\n",Token::tochars[e->op]);
#endif
assert(precedence[e->op] != PREC_zero);
assert(pr != PREC_zero);
//if (precedence[e->op] == 0) e->dump(0);
if (precedence[e->op] < pr ||
/* Despite precedence, we don't allow a<b<c expressions.
* They must be parenthesized.
*/
(pr == PREC_rel && precedence[e->op] == pr))
{
buf->writeByte('(');
e->toCBuffer(buf, hgs);
buf->writeByte(')');
}
else
e->toCBuffer(buf, hgs);
}
/**************************************************
* Write out argument list to buf.
*/
void argsToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs)
{
if (arguments)
{
for (size_t i = 0; i < arguments->dim; i++)
{ Expression *arg = (Expression *)arguments->data[i];
if (arg)
{ if (i)
buf->writeByte(',');
expToCBuffer(buf, hgs, arg, PREC_assign);
}
}
}
}
/**************************************************
* Write out argument types to buf.
*/
void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs)
{
if (arguments)
{ OutBuffer argbuf;
for (size_t i = 0; i < arguments->dim; i++)
{ Expression *arg = (Expression *)arguments->data[i];
if (i)
buf->writeByte(',');
argbuf.reset();
arg->type->toCBuffer2(&argbuf, hgs, 0);
buf->write(&argbuf);
}
}
}
/******************************** Expression **************************/
Expression::Expression(Loc loc, enum TOK op, int size)
: loc(loc)
{
//printf("Expression::Expression(op = %d) this = %p\n", op, this);
this->loc = loc;
this->op = op;
this->size = size;
type = NULL;
#if IN_LLVM
cachedLvalue = NULL;
#endif
}
Expression *Expression::syntaxCopy()
{
//printf("Expression::syntaxCopy()\n");
//dump(0);
return copy();
}
/*********************************
* Does *not* do a deep copy.
*/
Expression *Expression::copy()
{
Expression *e;
if (!size)
{
#ifdef DEBUG
fprintf(stdmsg, "No expression copy for: %s\n", toChars());
printf("op = %d\n", op);
dump(0);
#endif
assert(0);
}
e = (Expression *)mem.malloc(size);
//printf("Expression::copy(op = %d) e = %p\n", op, e);
return (Expression *)memcpy(e, this, size);
}
/**************************
* Semantically analyze Expression.
* Determine types, fold constants, etc.
*/
Expression *Expression::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("Expression::semantic() %s\n", toChars());
#endif
if (type)
type = type->semantic(loc, sc);
else
type = Type::tvoid;
return this;
}
void Expression::print()
{
fprintf(stdmsg, "%s\n", toChars());
fflush(stdmsg);
}
char *Expression::toChars()
{ OutBuffer *buf;
HdrGenState hgs;
memset(&hgs, 0, sizeof(hgs));
buf = new OutBuffer();
toCBuffer(buf, &hgs);
return buf->toChars();
}
void Expression::error(const char *format, ...)
{
if (type != Type::terror)
{
va_list ap;
va_start(ap, format);
::verror(loc, format, ap);
va_end( ap );
}
}
void Expression::warning(const char *format, ...)
{
if (type != Type::terror)
{
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
}
void Expression::rvalue()
{
if (type && type->toBasetype()->ty == Tvoid)
{ error("expression %s is void and has no value", toChars());
#if 0
dump(0);
halt();
#endif
type = Type::terror;
}
}
Expression *Expression::combine(Expression *e1, Expression *e2)
{
if (e1)
{
if (e2)
{
e1 = new CommaExp(e1->loc, e1, e2);
e1->type = e2->type;
}
}
else
e1 = e2;
return e1;
}
dinteger_t Expression::toInteger()
{
//printf("Expression %s\n", Token::toChars(op));
error("Integer constant expression expected instead of %s", toChars());
return 0;
}
uinteger_t Expression::toUInteger()
{
//printf("Expression %s\n", Token::toChars(op));
return (uinteger_t)toInteger();
}
real_t Expression::toReal()
{
error("Floating point constant expression expected instead of %s", toChars());
return 0;
}
real_t Expression::toImaginary()
{
error("Floating point constant expression expected instead of %s", toChars());
return 0;
}
complex_t Expression::toComplex()
{
error("Floating point constant expression expected instead of %s", toChars());
#ifdef IN_GCC
return complex_t(real_t(0)); // %% nicer
#else
return 0;
#endif
}
void Expression::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring(Token::toChars(op));
}
void Expression::toMangleBuffer(OutBuffer *buf)
{
error("expression %s is not a valid template value argument", toChars());
#ifdef DEBUG
dump(0);
#endif
}
/***************************************
* Return !=0 if expression is an lvalue.
*/
#if DMDV2
int Expression::isLvalue()
{
return 0;
}
#endif
/*******************************
* Give error if we're not an lvalue.
* If we can, convert expression to be an lvalue.
*/
Expression *Expression::toLvalue(Scope *sc, Expression *e)
{
if (!e)
e = this;
else if (!loc.filename)
loc = e->loc;
error("%s is not an lvalue", e->toChars());
return this;
}
Expression *Expression::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("Expression::modifiableLvalue() %s, type = %s\n", toChars(), type->toChars());
// See if this expression is a modifiable lvalue (i.e. not const)
#if DMDV2
if (type && (!type->isMutable() || !type->isAssignable()))
error("%s is not mutable", e->toChars());
#endif
return toLvalue(sc, e);
}
/************************************
* Detect cases where pointers to the stack can 'escape' the
* lifetime of the stack frame.
*/
void Expression::checkEscape()
{
}
void Expression::checkEscapeRef()
{
}
void Expression::checkScalar()
{
if (!type->isscalar())
error("'%s' is not a scalar, it is a %s", toChars(), type->toChars());
}
void Expression::checkNoBool()
{
if (type->toBasetype()->ty == Tbool)
error("operation not allowed on bool '%s'", toChars());
}
Expression *Expression::checkIntegral()
{
if (!type->isintegral())
{ error("'%s' is not of integral type, it is a %s", toChars(), type->toChars());
return new ErrorExp();
}
return this;
}
Expression *Expression::checkArithmetic()
{
if (!type->isintegral() && !type->isfloating())
{ error("'%s' is not of arithmetic type, it is a %s", toChars(), type->toChars());
return new ErrorExp();
}
return this;
}
void Expression::checkDeprecated(Scope *sc, Dsymbol *s)
{
s->checkDeprecated(loc, sc);
}
#if DMDV2
void Expression::checkPurity(Scope *sc, FuncDeclaration *f)
{
if (sc->func && sc->func->isPure() && !sc->intypeof && !f->isPure())
error("pure function '%s' cannot call impure function '%s'\n",
sc->func->toChars(), f->toChars());
}
#endif
/********************************
* Check for expressions that have no use.
* Input:
* flag 0 not going to use the result, so issue error message if no
* side effects
* 1 the result of the expression is used, but still check
* for useless subexpressions
* 2 do not issue error messages, just return !=0 if expression
* has side effects
*/
int Expression::checkSideEffect(int flag)
{
if (flag == 0)
{ if (op == TOKimport)
{
error("%s has no effect", toChars());
}
else
error("%s has no effect in expression (%s)",
Token::toChars(op), toChars());
}
return 0;
}
/*****************************
* Check that expression can be tested for true or false.
*/
Expression *Expression::checkToBoolean()
{
// Default is 'yes' - do nothing
#ifdef DEBUG
if (!type)
dump(0);
#endif
if (!type->checkBoolean())
{
error("expression %s of type %s does not have a boolean value", toChars(), type->toChars());
}
return this;
}
/****************************
*/
Expression *Expression::checkToPointer()
{
Expression *e;
Type *tb;
//printf("Expression::checkToPointer()\n");
e = this;
// If C static array, convert to pointer
tb = type->toBasetype();
if (tb->ty == Tsarray)
{ TypeSArray *ts = (TypeSArray *)tb;
if (ts->size(loc) == 0)
e = new NullExp(loc);
else
e = new AddrExp(loc, this);
e->type = ts->next->pointerTo();
}
return e;
}
/******************************
* Take address of expression.
*/
Expression *Expression::addressOf(Scope *sc)
{
Expression *e;
//printf("Expression::addressOf()\n");
e = toLvalue(sc, NULL);
e = new AddrExp(loc, e);
e->type = type->pointerTo();
return e;
}
/******************************
* If this is a reference, dereference it.
*/
Expression *Expression::deref()
{
//printf("Expression::deref()\n");
// type could be null if forward referencing an 'auto' variable
if (type && type->ty == Treference)
{
Expression *e = new PtrExp(loc, this);
e->type = ((TypeReference *)type)->next;
return e;
}
return this;
}
/********************************
* Does this expression statically evaluate to a boolean TRUE or FALSE?
*/
int Expression::isBool(int result)
{
return FALSE;
}
/********************************
* Does this expression result in either a 1 or a 0?
*/
int Expression::isBit()
{
return FALSE;
}
/********************************
* Can this expression throw an exception?
* Valid only after semantic() pass.
*/
int Expression::canThrow()
{
#if DMDV2
return FALSE;
#else
return TRUE;
#endif
}
Expressions *Expression::arraySyntaxCopy(Expressions *exps)
{ Expressions *a = NULL;
if (exps)
{
a = new Expressions();
a->setDim(exps->dim);
for (int i = 0; i < a->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
e = e->syntaxCopy();
a->data[i] = e;
}
}
return a;
}
/******************************** IntegerExp **************************/
IntegerExp::IntegerExp(Loc loc, dinteger_t value, Type *type)
: Expression(loc, TOKint64, sizeof(IntegerExp))
{
//printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type->toChars() : "");
if (type && !type->isscalar())
{
//printf("%s, loc = %d\n", toChars(), loc.linnum);
if (type->ty != Terror)
error("integral constant must be scalar type, not %s", type->toChars());
type = Type::terror;
}
this->type = type;
this->value = value;
}
IntegerExp::IntegerExp(dinteger_t value)
: Expression(0, TOKint64, sizeof(IntegerExp))
{
this->type = Type::tint32;
this->value = value;
}
int IntegerExp::equals(Object *o)
{ IntegerExp *ne;
if (this == o ||
(((Expression *)o)->op == TOKint64 &&
((ne = (IntegerExp *)o), type->equals(ne->type)) &&
value == ne->value))
return 1;
return 0;
}
char *IntegerExp::toChars()
{
#if 1
return Expression::toChars();
#else
static char buffer[sizeof(value) * 3 + 1];
sprintf(buffer, "%jd", value);
return buffer;
#endif
}
dinteger_t IntegerExp::toInteger()
{ Type *t;
t = type;
while (t)
{
switch (t->ty)
{
case Tbit:
case Tbool: value = (value != 0); break;
case Tint8: value = (d_int8) value; break;
case Tchar:
case Tuns8: value = (d_uns8) value; break;
case Tint16: value = (d_int16) value; break;
case Twchar:
case Tuns16: value = (d_uns16) value; break;
case Tint32: value = (d_int32) value; break;
case Tdchar:
case Tuns32: value = (d_uns32) value; break;
case Tint64: value = (d_int64) value; break;
case Tuns64: value = (d_uns64) value; break;
case Tpointer:
if (PTRSIZE == 4)
value = (d_uns32) value;
else if (PTRSIZE == 8)
value = (d_uns64) value;
else
assert(0);
break;
case Tenum:
{
TypeEnum *te = (TypeEnum *)t;
t = te->sym->memtype;
continue;
}
case Ttypedef:
{
TypeTypedef *tt = (TypeTypedef *)t;
t = tt->sym->basetype;
continue;
}
default:
/* This can happen if errors, such as
* the type is painted on like in fromConstInitializer().
*/
if (!global.errors)
{ type->print();
assert(0);
}
break;
}
break;
}
return value;
}
real_t IntegerExp::toReal()
{
Type *t;
toInteger();
t = type->toBasetype();
if (t->ty == Tuns64)
return (real_t)(d_uns64)value;
else
return (real_t)(d_int64)value;
}
real_t IntegerExp::toImaginary()
{
return (real_t) 0;
}
complex_t IntegerExp::toComplex()
{
return toReal();
}
int IntegerExp::isBool(int result)
{
int r = toInteger() != 0;
return result ? r : !r;
}
Expression *IntegerExp::semantic(Scope *sc)
{
if (!type)
{
// Determine what the type of this number is
dinteger_t number = value;
if (number & 0x8000000000000000LL)
type = Type::tuns64;
else if (number & 0xFFFFFFFF80000000LL)
type = Type::tint64;
else
type = Type::tint32;
}
else
{ if (!type->deco)
type = type->semantic(loc, sc);
}
return this;
}
Expression *IntegerExp::toLvalue(Scope *sc, Expression *e)
{
if (!e)
e = this;
else if (!loc.filename)
loc = e->loc;
e->error("constant %s is not an lvalue", e->toChars());
return this;
}
void IntegerExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
dinteger_t v = toInteger();
if (type)
{ Type *t = type;
L1:
switch (t->ty)
{
case Tenum:
{ TypeEnum *te = (TypeEnum *)t;
buf->printf("cast(%s)", te->sym->toChars());
t = te->sym->memtype;
goto L1;
}
case Ttypedef:
{ TypeTypedef *tt = (TypeTypedef *)t;
buf->printf("cast(%s)", tt->sym->toChars());
t = tt->sym->basetype;
goto L1;
}
case Twchar: // BUG: need to cast(wchar)
case Tdchar: // BUG: need to cast(dchar)
if ((uinteger_t)v > 0xFF)
{
buf->printf("'\\U%08x'", (unsigned)v);
break;
}
case Tchar:
if (v == '\'')
buf->writestring("'\\''");
else if (isprint(v) && v != '\\')
buf->printf("'%c'", (int)v);
else
buf->printf("'\\x%02x'", (int)v);
break;
case Tint8:
buf->writestring("cast(byte)");
goto L2;
case Tint16:
buf->writestring("cast(short)");
goto L2;
case Tint32:
L2:
buf->printf("%d", (int)v);
break;
case Tuns8:
buf->writestring("cast(ubyte)");
goto L3;
case Tuns16:
buf->writestring("cast(ushort)");
goto L3;
case Tuns32:
L3:
buf->printf("%du", (unsigned)v);
break;
case Tint64:
buf->printf("%jdL", v);
break;
case Tuns64:
L4:
buf->printf("%juLU", v);
break;
case Tbit:
case Tbool:
buf->writestring((char *)(v ? "true" : "false"));
break;
case Tpointer:
buf->writestring("cast(");
buf->writestring(t->toChars());
buf->writeByte(')');
if (PTRSIZE == 4)
goto L3;
else if (PTRSIZE == 8)
goto L4;
else
assert(0);
default:
/* This can happen if errors, such as
* the type is painted on like in fromConstInitializer().
*/
if (!global.errors)
{
#ifdef DEBUG
t->print();
#endif
assert(0);
}
break;
}
}
else if (v & 0x8000000000000000LL)
buf->printf("0x%jx", v);
else
buf->printf("%jd", v);
}
void IntegerExp::toMangleBuffer(OutBuffer *buf)
{
if ((sinteger_t)value < 0)
buf->printf("N%jd", -value);
else
{
/* This is an awful hack to maintain backwards compatibility.
* There really always should be an 'i' before a number, but
* there wasn't in earlier implementations, so to maintain
* backwards compatibility it is only done if necessary to disambiguate.
* See bugzilla 3029
*/
if (buf->offset > 0 && isdigit(buf->data[buf->offset - 1]))
buf->writeByte('i');
buf->printf("%jd", value);
}
}
/******************************** ErrorExp **************************/
/* Use this expression for error recovery.
* It should behave as a 'sink' to prevent further cascaded error messages.
*/
ErrorExp::ErrorExp()
: IntegerExp(0, 0, Type::terror)
{
op = TOKerror;
}
void ErrorExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("__error");
}
/******************************** RealExp **************************/
RealExp::RealExp(Loc loc, real_t value, Type *type)
: Expression(loc, TOKfloat64, sizeof(RealExp))
{
//printf("RealExp::RealExp(%Lg)\n", value);
this->value = value;
this->type = type;
}
char *RealExp::toChars()
{
char buffer[sizeof(value) * 3 + 8 + 1 + 1];
#ifdef IN_GCC
value.format(buffer, sizeof(buffer));
if (type->isimaginary())
strcat(buffer, "i");
#else
sprintf(buffer, type->isimaginary() ? "%Lgi" : "%Lg", value);
#endif
assert(strlen(buffer) < sizeof(buffer));
return mem.strdup(buffer);
}
dinteger_t RealExp::toInteger()
{
#ifdef IN_GCC
return toReal().toInt();
#else
return (sinteger_t) toReal();
#endif
}
uinteger_t RealExp::toUInteger()
{
#ifdef IN_GCC
return (uinteger_t) toReal().toInt();
#else
return (uinteger_t) toReal();
#endif
}
real_t RealExp::toReal()
{
return type->isreal() ? value : 0;
}
real_t RealExp::toImaginary()
{
return type->isreal() ? 0 : value;
}
complex_t RealExp::toComplex()
{
#ifdef __DMC__
return toReal() + toImaginary() * I;
#else
return complex_t(toReal(), toImaginary());
#endif
}
/********************************
* Test to see if two reals are the same.
* Regard NaN's as equivalent.
* Regard +0 and -0 as different.
*/
int RealEquals(real_t x1, real_t x2)
{
#if __APPLE__
return (__inline_isnan(x1) && __inline_isnan(x2)) ||
#else
return // special case nans
(isnan(x1) && isnan(x2)) ||
#endif
// and zero, in order to distinguish +0 from -0
(x1 == 0 && x2 == 0 && 1./x1 == 1./x2) ||
// otherwise just compare
(x1 != 0. && x1 == x2);
}
int RealExp::equals(Object *o)
{ RealExp *ne;
if (this == o ||
(((Expression *)o)->op == TOKfloat64 &&
((ne = (RealExp *)o), type->equals(ne->type)) &&
RealEquals(value, ne->value)
)
)
return 1;
return 0;
}
Expression *RealExp::semantic(Scope *sc)
{
if (!type)
type = Type::tfloat64;
else
type = type->semantic(loc, sc);
return this;
}
int RealExp::isBool(int result)
{
#ifdef IN_GCC
return result ? (! value.isZero()) : (value.isZero());
#else
return result ? (value != 0)
: (value == 0);
#endif
}
void floatToBuffer(OutBuffer *buf, Type *type, real_t value)
{
/* In order to get an exact representation, try converting it
* to decimal then back again. If it matches, use it.
* If it doesn't, fall back to hex, which is
* always exact.
*/
char buffer[25];
sprintf(buffer, "%Lg", value);
assert(strlen(buffer) < sizeof(buffer));
#if _WIN32 && __DMC__
char *save = __locale_decpoint;
__locale_decpoint = ".";
real_t r = strtold(buffer, NULL);
__locale_decpoint = save;
#else
real_t r = strtold(buffer, NULL);
#endif
if (r == value) // if exact duplication
buf->writestring(buffer);
else
{
#ifdef __HAIKU__ // broken printf workaround
char buffer2[25];
char *ptr = (char *)&value;
for(int i = 0; i < sizeof(value); i++)
snprintf(buffer2, sizeof(char), "%x", ptr[i]);
buf->writestring(buffer2);
#else
buf->printf("%La", value); // ensure exact duplication
#endif
}
if (type)
{
Type *t = type->toBasetype();
switch (t->ty)
{
case Tfloat32:
case Timaginary32:
case Tcomplex32:
buf->writeByte('F');
break;
case Tfloat80:
case Timaginary80:
case Tcomplex80:
buf->writeByte('L');
break;
default:
break;
}
if (t->isimaginary())
buf->writeByte('i');
}
}
void RealExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
floatToBuffer(buf, type, value);
}
void realToMangleBuffer(OutBuffer *buf, real_t value)
{
/* Rely on %A to get portable mangling.
* Must munge result to get only identifier characters.
*
* Possible values from %A => mangled result
* NAN => NAN
* -INF => NINF
* INF => INF
* -0X1.1BC18BA997B95P+79 => N11BC18BA997B95P79
* 0X1.9P+2 => 19P2
*/
#if __APPLE__
if (__inline_isnan(value))
#else
if (isnan(value))
#endif
buf->writestring("NAN"); // no -NAN bugs
else
{
char buffer[32];
int n = sprintf(buffer, "%LA", value);
assert(n > 0 && n < sizeof(buffer));
for (int i = 0; i < n; i++)
{ char c = buffer[i];
switch (c)
{
case '-':
buf->writeByte('N');
break;
case '+':
case 'X':
case '.':
break;
case '0':
if (i < 2)
break; // skip leading 0X
default:
buf->writeByte(c);
break;
}
}
}
}
void RealExp::toMangleBuffer(OutBuffer *buf)
{
buf->writeByte('e');
realToMangleBuffer(buf, value);
}
/******************************** ComplexExp **************************/
ComplexExp::ComplexExp(Loc loc, complex_t value, Type *type)
: Expression(loc, TOKcomplex80, sizeof(ComplexExp))
{
this->value = value;
this->type = type;
//printf("ComplexExp::ComplexExp(%s)\n", toChars());
}
char *ComplexExp::toChars()
{
char buffer[sizeof(value) * 3 + 8 + 1];
#ifdef IN_GCC
char buf1[sizeof(value) * 3 + 8 + 1];
char buf2[sizeof(value) * 3 + 8 + 1];
creall(value).format(buf1, sizeof(buf1));
cimagl(value).format(buf2, sizeof(buf2));
sprintf(buffer, "(%s+%si)", buf1, buf2);
#else
sprintf(buffer, "(%Lg+%Lgi)", creall(value), cimagl(value));
assert(strlen(buffer) < sizeof(buffer));
#endif
return mem.strdup(buffer);
}
dinteger_t ComplexExp::toInteger()
{
#ifdef IN_GCC
return (sinteger_t) toReal().toInt();
#else
return (sinteger_t) toReal();
#endif
}
uinteger_t ComplexExp::toUInteger()
{
#ifdef IN_GCC
return (uinteger_t) toReal().toInt();
#else
return (uinteger_t) toReal();
#endif
}
real_t ComplexExp::toReal()
{
return creall(value);
}
real_t ComplexExp::toImaginary()
{
return cimagl(value);
}
complex_t ComplexExp::toComplex()
{
return value;
}
int ComplexExp::equals(Object *o)
{ ComplexExp *ne;
if (this == o ||
(((Expression *)o)->op == TOKcomplex80 &&
((ne = (ComplexExp *)o), type->equals(ne->type)) &&
RealEquals(creall(value), creall(ne->value)) &&
RealEquals(cimagl(value), cimagl(ne->value))
)
)
return 1;
return 0;
}
Expression *ComplexExp::semantic(Scope *sc)
{
if (!type)
type = Type::tcomplex80;
else
type = type->semantic(loc, sc);
return this;
}
int ComplexExp::isBool(int result)
{
if (result)
return (bool)(value);
else
return !value;
}
void ComplexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
/* Print as:
* (re+imi)
*/
#ifdef IN_GCC
char buf1[sizeof(value) * 3 + 8 + 1];
char buf2[sizeof(value) * 3 + 8 + 1];
creall(value).format(buf1, sizeof(buf1));
cimagl(value).format(buf2, sizeof(buf2));
buf->printf("(%s+%si)", buf1, buf2);
#else
buf->writeByte('(');
floatToBuffer(buf, type, creall(value));
buf->writeByte('+');
floatToBuffer(buf, type, cimagl(value));
buf->writestring("i)");
#endif
}
void ComplexExp::toMangleBuffer(OutBuffer *buf)
{
buf->writeByte('c');
real_t r = toReal();
realToMangleBuffer(buf, r);
buf->writeByte('c'); // separate the two
r = toImaginary();
realToMangleBuffer(buf, r);
}
/******************************** IdentifierExp **************************/
IdentifierExp::IdentifierExp(Loc loc, Identifier *ident)
: Expression(loc, TOKidentifier, sizeof(IdentifierExp))
{
this->ident = ident;
}
Expression *IdentifierExp::semantic(Scope *sc)
{
Dsymbol *s;
Dsymbol *scopesym;
#if LOGSEMANTIC
printf("IdentifierExp::semantic('%s')\n", ident->toChars());
#endif
s = sc->search(loc, ident, &scopesym);
if (s)
{ Expression *e;
WithScopeSymbol *withsym;
/* See if the symbol was a member of an enclosing 'with'
*/
withsym = scopesym->isWithScopeSymbol();
if (withsym)
{
s = s->toAlias();
// Same as wthis.ident
if (s->needThis() || s->isTemplateDeclaration())
{
e = new VarExp(loc, withsym->withstate->wthis);
e = new DotIdExp(loc, e, ident);
}
else
{ Type *t = withsym->withstate->wthis->type;
if (t->ty == Tpointer)
t = ((TypePointer *)t)->next;
e = typeDotIdExp(loc, t, ident);
}
}
else
{
if (!s->parent && scopesym->isArrayScopeSymbol())
{ // Kludge to run semantic() here because
// ArrayScopeSymbol::search() doesn't have access to sc.
s->semantic(sc);
}
/* If f is really a function template,
* then replace f with the function template declaration.
*/
FuncDeclaration *f = s->isFuncDeclaration();
if (f && f->parent)
{ TemplateInstance *ti = f->parent->isTemplateInstance();
if (ti &&
!ti->isTemplateMixin() &&
(ti->name == f->ident ||
ti->toAlias()->ident == f->ident)
&&
ti->tempdecl && ti->tempdecl->onemember)
{
TemplateDeclaration *tempdecl = ti->tempdecl;
if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's
tempdecl = tempdecl->overroot; // then get the start
e = new TemplateExp(loc, tempdecl);
e = e->semantic(sc);
return e;
}
}
e = new DsymbolExp(loc, s);
}
return e->semantic(sc);
}
#if DMDV2
if (ident == Id::ctfe)
{ // Create the magic __ctfe bool variable
VarDeclaration *vd = new VarDeclaration(loc, Type::tbool, Id::ctfe, NULL);
Expression *e = new VarExp(loc, vd);
e = e->semantic(sc);
return e;
}
#endif
const char *n = importHint(ident->toChars());
if (n)
error("'%s' is not defined, perhaps you need to import %s; ?", ident->toChars(), n);
else
{
s = sc->search_correct(ident);
if (s)
error("undefined identifier %s, did you mean %s %s?", ident->toChars(), s->kind(), s->toChars());
else
error("undefined identifier %s", ident->toChars());
}
return new ErrorExp();
}
char *IdentifierExp::toChars()
{
return ident->toChars();
}
void IdentifierExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (hgs->hdrgen)
buf->writestring(ident->toHChars2());
else
buf->writestring(ident->toChars());
}
#if DMDV2
int IdentifierExp::isLvalue()
{
return 1;
}
#endif
Expression *IdentifierExp::toLvalue(Scope *sc, Expression *e)
{
#if 0
tym = tybasic(e1->ET->Tty);
if (!(tyscalar(tym) ||
tym == TYstruct ||
tym == TYarray && e->Eoper == TOKaddr))
synerr(EM_lvalue); // lvalue expected
#endif
return this;
}
/******************************** DollarExp **************************/
DollarExp::DollarExp(Loc loc)
: IdentifierExp(loc, Id::dollar)
{
}
/******************************** DsymbolExp **************************/
DsymbolExp::DsymbolExp(Loc loc, Dsymbol *s)
: Expression(loc, TOKdsymbol, sizeof(DsymbolExp))
{
this->s = s;
}
Expression *DsymbolExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("DsymbolExp::semantic('%s')\n", s->toChars());
#endif
Lagain:
EnumMember *em;
Expression *e;
VarDeclaration *v;
FuncDeclaration *f;
FuncLiteralDeclaration *fld;
Declaration *d;
ClassDeclaration *cd;
ClassDeclaration *thiscd = NULL;
Import *imp;
Package *pkg;
Type *t;
//printf("DsymbolExp:: %p '%s' is a symbol\n", this, toChars());
//printf("s = '%s', s->kind = '%s'\n", s->toChars(), s->kind());
if (type)
return this;
if (!s->isFuncDeclaration()) // functions are checked after overloading
checkDeprecated(sc, s);
s = s->toAlias();
//printf("s = '%s', s->kind = '%s', s->needThis() = %p\n", s->toChars(), s->kind(), s->needThis());
if (!s->isFuncDeclaration())
checkDeprecated(sc, s);
if (sc->func)
thiscd = sc->func->parent->isClassDeclaration();
// BUG: This should happen after overload resolution for functions, not before
if (s->needThis())
{
if (hasThis(sc)
#if DMDV2
&& !s->isFuncDeclaration()
#endif
)
{
// Supply an implicit 'this', as in
// this.ident
DotVarExp *de;
de = new DotVarExp(loc, new ThisExp(loc), s->isDeclaration());
return de->semantic(sc);
}
}
em = s->isEnumMember();
if (em)
{
e = em->value->copy();
e = e->semantic(sc);
return e;
}
v = s->isVarDeclaration();
if (v)
{
//printf("Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
if (!type)
{ if ((!v->type || !v->type->deco) && v->scope)
v->semantic(v->scope);
type = v->type;
if (!v->type)
{ error("forward reference of %s", v->toChars());
type = Type::terror;
}
}
if (v->isSameAsInitializer() && type->toBasetype()->ty != Tsarray)
{
if (v->init)
{
if (v->inuse)
{
error("circular reference to '%s'", v->toChars());
type = Type::tint32;
return this;
}
ExpInitializer *ei = v->init->isExpInitializer();
if (ei)
{
e = ei->exp->copy(); // make copy so we can change loc
if (e->op == TOKstring || !e->type)
e = e->semantic(sc);
e = e->implicitCastTo(sc, type);
e->loc = loc;
return e;
}
}
else
{
e = type->defaultInit();
e->loc = loc;
return e;
}
}
e = new VarExp(loc, v);
e->type = type;
e = e->semantic(sc);
return e->deref();
}
fld = s->isFuncLiteralDeclaration();
if (fld)
{ //printf("'%s' is a function literal\n", fld->toChars());
e = new FuncExp(loc, fld);
return e->semantic(sc);
}
f = s->isFuncDeclaration();
if (f)
{ //printf("'%s' is a function\n", f->toChars());
if (!f->originalType && f->scope) // semantic not yet run
f->semantic(f->scope);
if (f->isUnitTestDeclaration())
{
error("cannot call unittest function %s", toChars());
return new ErrorExp();
}
if (!f->type->deco)
{
error("forward reference to %s", toChars());
return new ErrorExp();
}
return new VarExp(loc, f);
}
cd = s->isClassDeclaration();
if (cd && thiscd && cd->isBaseOf(thiscd, NULL) && sc->func->needThis())
{
// We need to add an implicit 'this' if cd is this class or a base class.
DotTypeExp *dte;
dte = new DotTypeExp(loc, new ThisExp(loc), s);
return dte->semantic(sc);
}
imp = s->isImport();
if (imp)
{
if (!imp->pkg)
{ error("forward reference of import %s", imp->toChars());
return this;
}
ScopeExp *ie = new ScopeExp(loc, imp->pkg);
return ie->semantic(sc);
}
pkg = s->isPackage();
if (pkg)
{
ScopeExp *ie;
ie = new ScopeExp(loc, pkg);
return ie->semantic(sc);
}
Module *mod = s->isModule();
if (mod)
{
ScopeExp *ie;
ie = new ScopeExp(loc, mod);
return ie->semantic(sc);
}
t = s->getType();
if (t)
{
return new TypeExp(loc, t);
}
TupleDeclaration *tup = s->isTupleDeclaration();
if (tup)
{
e = new TupleExp(loc, tup);
e = e->semantic(sc);
return e;
}
TemplateInstance *ti = s->isTemplateInstance();
if (ti && !global.errors)
{ if (!ti->semanticRun)
ti->semantic(sc);
s = ti->inst->toAlias();
if (!s->isTemplateInstance())
goto Lagain;
e = new ScopeExp(loc, ti);
e = e->semantic(sc);
return e;
}
TemplateDeclaration *td = s->isTemplateDeclaration();
if (td)
{
e = new TemplateExp(loc, td);
e = e->semantic(sc);
return e;
}
Lerr:
error("%s '%s' is not a variable", s->kind(), s->toChars());
type = Type::terror;
return this;
}
char *DsymbolExp::toChars()
{
return s->toChars();
}
void DsymbolExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring(s->toChars());
}
#if DMDV2
int DsymbolExp::isLvalue()
{
return 1;
}
#endif
Expression *DsymbolExp::toLvalue(Scope *sc, Expression *e)
{
#if 0
tym = tybasic(e1->ET->Tty);
if (!(tyscalar(tym) ||
tym == TYstruct ||
tym == TYarray && e->Eoper == TOKaddr))
synerr(EM_lvalue); // lvalue expected
#endif
return this;
}
/******************************** ThisExp **************************/
ThisExp::ThisExp(Loc loc)
: Expression(loc, TOKthis, sizeof(ThisExp))
{
var = NULL;
}
Expression *ThisExp::semantic(Scope *sc)
{ FuncDeclaration *fd;
FuncDeclaration *fdthis;
int nested = 0;
#if LOGSEMANTIC
printf("ThisExp::semantic()\n");
#endif
if (type)
{ //assert(global.errors || var);
return this;
}
/* Special case for typeof(this) and typeof(super) since both
* should work even if they are not inside a non-static member function
*/
if (sc->intypeof)
{
// Find enclosing struct or class
for (Dsymbol *s = sc->parent; 1; s = s->parent)
{
ClassDeclaration *cd;
StructDeclaration *sd;
if (!s)
{
error("%s is not in a struct or class scope", toChars());
goto Lerr;
}
cd = s->isClassDeclaration();
if (cd)
{
type = cd->type;
return this;
}
sd = s->isStructDeclaration();
if (sd)
{
type = sd->type->pointerTo();
return this;
}
}
}
fdthis = sc->parent->isFuncDeclaration();
fd = hasThis(sc); // fd is the uplevel function with the 'this' variable
if (!fd)
goto Lerr;
assert(fd->vthis);
var = fd->vthis;
assert(var->parent);
type = var->type;
var->isVarDeclaration()->checkNestedReference(sc, loc);
#if 0
if (fd != fdthis) // if nested
{
fdthis->getLevel(loc, fd);
fd->vthis->nestedref = 1;
fd->nestedFrameRef = 1;
}
#endif
if (!sc->intypeof)
sc->callSuper |= CSXthis;
return this;
Lerr:
error("'this' is only defined in non-static member functions, not %s", sc->parent->toChars());
type = Type::terror;
return this;
}
int ThisExp::isBool(int result)
{
return result ? TRUE : FALSE;
}
void ThisExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("this");
}
#if DMDV2
int ThisExp::isLvalue()
{
return 1;
}
#endif
Expression *ThisExp::toLvalue(Scope *sc, Expression *e)
{
return this;
}
/******************************** SuperExp **************************/
SuperExp::SuperExp(Loc loc)
: ThisExp(loc)
{
op = TOKsuper;
}
Expression *SuperExp::semantic(Scope *sc)
{ FuncDeclaration *fd;
FuncDeclaration *fdthis;
ClassDeclaration *cd;
Dsymbol *s;
#if LOGSEMANTIC
printf("SuperExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
/* Special case for typeof(this) and typeof(super) since both
* should work even if they are not inside a non-static member function
*/
if (sc->intypeof)
{
// Find enclosing class
for (Dsymbol *s = sc->parent; 1; s = s->parent)
{
ClassDeclaration *cd;
if (!s)
{
error("%s is not in a class scope", toChars());
goto Lerr;
}
cd = s->isClassDeclaration();
if (cd)
{
cd = cd->baseClass;
if (!cd)
{ error("class %s has no 'super'", s->toChars());
goto Lerr;
}
type = cd->type;
return this;
}
}
}
fdthis = sc->parent->isFuncDeclaration();
fd = hasThis(sc);
if (!fd)
goto Lerr;
assert(fd->vthis);
var = fd->vthis;
assert(var->parent);
s = fd->toParent();
while (s && s->isTemplateInstance())
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)
{
error("no base class for %s", cd->toChars());
type = fd->vthis->type;
}
else
{
type = cd->baseClass->type;
}
var->isVarDeclaration()->checkNestedReference(sc, loc);
#if 0
if (fd != fdthis)
{
fdthis->getLevel(loc, fd);
fd->vthis->nestedref = 1;
fd->nestedFrameRef = 1;
}
#endif
if (!sc->intypeof)
sc->callSuper |= CSXsuper;
return this;
Lerr:
error("'super' is only allowed in non-static class member functions");
type = Type::tint32;
return this;
}
void SuperExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("super");
}
/******************************** NullExp **************************/
NullExp::NullExp(Loc loc, Type *type)
: Expression(loc, TOKnull, sizeof(NullExp))
{
committed = 0;
this->type = type;
}
Expression *NullExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("NullExp::semantic('%s')\n", toChars());
#endif
// NULL is the same as (void *)0
if (!type)
type = Type::tvoid->pointerTo();
return this;
}
int NullExp::isBool(int result)
{
return result ? FALSE : TRUE;
}
void NullExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("null");
}
void NullExp::toMangleBuffer(OutBuffer *buf)
{
buf->writeByte('n');
}
/******************************** StringExp **************************/
StringExp::StringExp(Loc loc, char *string)
: Expression(loc, TOKstring, sizeof(StringExp))
{
this->string = string;
this->len = strlen(string);
this->sz = 1;
this->committed = 0;
this->postfix = 0;
}
StringExp::StringExp(Loc loc, void *string, size_t len)
: Expression(loc, TOKstring, sizeof(StringExp))
{
this->string = string;
this->len = len;
this->sz = 1;
this->committed = 0;
this->postfix = 0;
}
StringExp::StringExp(Loc loc, void *string, size_t len, unsigned char postfix)
: Expression(loc, TOKstring, sizeof(StringExp))
{
this->string = string;
this->len = len;
this->sz = 1;
this->committed = 0;
this->postfix = postfix;
}
#if 0
Expression *StringExp::syntaxCopy()
{
printf("StringExp::syntaxCopy() %s\n", toChars());
return copy();
}
#endif
int StringExp::equals(Object *o)
{
//printf("StringExp::equals('%s')\n", o->toChars());
if (o && o->dyncast() == DYNCAST_EXPRESSION)
{ Expression *e = (Expression *)o;
if (e->op == TOKstring)
{
return compare(o) == 0;
}
}
return FALSE;
}
char *StringExp::toChars()
{
OutBuffer buf;
HdrGenState hgs;
char *p;
memset(&hgs, 0, sizeof(hgs));
toCBuffer(&buf, &hgs);
buf.writeByte(0);
p = (char *)buf.data;
buf.data = NULL;
return p;
}
Expression *StringExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("StringExp::semantic() %s\n", toChars());
#endif
if (!type)
{ OutBuffer buffer;
size_t newlen = 0;
const char *p;
size_t u;
unsigned c;
switch (postfix)
{
case 'd':
for (u = 0; u < len;)
{
p = utf_decodeChar((unsigned char *)string, len, &u, &c);
if (p)
{ error("%s", p);
break;
}
else
{ buffer.write4(c);
newlen++;
}
}
buffer.write4(0);
string = buffer.extractData();
len = newlen;
sz = 4;
type = new TypeSArray(Type::tdchar, new IntegerExp(loc, len, Type::tindex));
committed = 1;
break;
case 'w':
for (u = 0; u < len;)
{
p = utf_decodeChar((unsigned char *)string, len, &u, &c);
if (p)
{ error("%s", p);
break;
}
else
{ buffer.writeUTF16(c);
newlen++;
if (c >= 0x10000)
newlen++;
}
}
buffer.writeUTF16(0);
string = buffer.extractData();
len = newlen;
sz = 2;
type = new TypeSArray(Type::twchar, new IntegerExp(loc, len, Type::tindex));
committed = 1;
break;
case 'c':
committed = 1;
default:
type = new TypeSArray(Type::tchar, new IntegerExp(loc, len, Type::tindex));
break;
}
type = type->semantic(loc, sc);
}
return this;
}
/****************************************
* Convert string to char[].
*/
StringExp *StringExp::toUTF8(Scope *sc)
{
if (sz != 1)
{ // Convert to UTF-8 string
committed = 0;
Expression *e = castTo(sc, Type::tchar->arrayOf());
e = e->optimize(WANTvalue);
assert(e->op == TOKstring);
StringExp *se = (StringExp *)e;
assert(se->sz == 1);
return se;
}
return this;
}
int StringExp::compare(Object *obj)
{
// Used to sort case statement expressions so we can do an efficient lookup
StringExp *se2 = (StringExp *)(obj);
// This is a kludge so isExpression() in template.c will return 5
// for StringExp's.
if (!se2)
return 5;
assert(se2->op == TOKstring);
int len1 = len;
int len2 = se2->len;
if (len1 == len2)
{
switch (sz)
{
case 1:
return memcmp((char *)string, (char *)se2->string, len1);
case 2:
{ unsigned u;
d_wchar *s1 = (d_wchar *)string;
d_wchar *s2 = (d_wchar *)se2->string;
for (u = 0; u < len; u++)
{
if (s1[u] != s2[u])
return s1[u] - s2[u];
}
}
case 4:
{ unsigned u;
d_dchar *s1 = (d_dchar *)string;
d_dchar *s2 = (d_dchar *)se2->string;
for (u = 0; u < len; u++)
{
if (s1[u] != s2[u])
return s1[u] - s2[u];
}
}
break;
default:
assert(0);
}
}
return len1 - len2;
}
int StringExp::isBool(int result)
{
return result ? TRUE : FALSE;
}
unsigned StringExp::charAt(size_t i)
{ unsigned value;
switch (sz)
{
case 1:
value = ((unsigned char *)string)[i];
break;
case 2:
value = ((unsigned short *)string)[i];
break;
case 4:
value = ((unsigned int *)string)[i];
break;
default:
assert(0);
break;
}
return value;
}
void StringExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writeByte('"');
for (size_t i = 0; i < len; i++)
{ unsigned c = charAt(i);
switch (c)
{
case '"':
case '\\':
if (!hgs->console)
buf->writeByte('\\');
default:
if (c <= 0xFF)
{ if (c <= 0x7F && (isprint(c) || hgs->console))
buf->writeByte(c);
else
buf->printf("\\x%02x", c);
}
else if (c <= 0xFFFF)
buf->printf("\\x%02x\\x%02x", c & 0xFF, c >> 8);
else
buf->printf("\\x%02x\\x%02x\\x%02x\\x%02x",
c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24);
break;
}
}
buf->writeByte('"');
if (postfix)
buf->writeByte(postfix);
}
void StringExp::toMangleBuffer(OutBuffer *buf)
{ char m;
OutBuffer tmp;
const char *p;
unsigned c;
size_t u;
unsigned char *q;
unsigned qlen;
/* Write string in UTF-8 format
*/
switch (sz)
{ case 1:
m = 'a';
q = (unsigned char *)string;
qlen = len;
break;
case 2:
m = 'w';
for (u = 0; u < len; )
{
p = utf_decodeWchar((unsigned short *)string, len, &u, &c);
if (p)
error("%s", p);
else
tmp.writeUTF8(c);
}
q = tmp.data;
qlen = tmp.offset;
break;
case 4:
m = 'd';
for (u = 0; u < len; u++)
{
c = ((unsigned *)string)[u];
if (!utf_isValidDchar(c))
error("invalid UCS-32 char \\U%08x", c);
else
tmp.writeUTF8(c);
}
q = tmp.data;
qlen = tmp.offset;
break;
default:
assert(0);
}
buf->writeByte(m);
buf->printf("%d_", qlen);
for (size_t i = 0; i < qlen; i++)
buf->printf("%02x", q[i]);
}
/************************ ArrayLiteralExp ************************************/
// [ e1, e2, e3, ... ]
ArrayLiteralExp::ArrayLiteralExp(Loc loc, Expressions *elements)
: Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
{
this->elements = elements;
}
ArrayLiteralExp::ArrayLiteralExp(Loc loc, Expression *e)
: Expression(loc, TOKarrayliteral, sizeof(ArrayLiteralExp))
{
elements = new Expressions;
elements->push(e);
}
Expression *ArrayLiteralExp::syntaxCopy()
{
return new ArrayLiteralExp(loc, arraySyntaxCopy(elements));
}
Expression *ArrayLiteralExp::semantic(Scope *sc)
{ Expression *e;
Type *t0 = NULL;
#if LOGSEMANTIC
printf("ArrayLiteralExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
// Run semantic() on each element
for (int i = 0; i < elements->dim; i++)
{ e = (Expression *)elements->data[i];
e = e->semantic(sc);
elements->data[i] = (void *)e;
}
expandTuples(elements);
for (int i = 0; i < elements->dim; i++)
{ e = (Expression *)elements->data[i];
if (!e->type)
error("%s has no value", e->toChars());
e = resolveProperties(sc, e);
unsigned char committed = 1;
if (e->op == TOKstring)
committed = ((StringExp *)e)->committed;
if (!t0)
{ t0 = e->type;
// Convert any static arrays to dynamic arrays
if (t0->ty == Tsarray)
{
t0 = ((TypeSArray *)t0)->next->arrayOf();
e = e->implicitCastTo(sc, t0);
}
}
else
e = e->implicitCastTo(sc, t0);
if (!committed && e->op == TOKstring)
{ StringExp *se = (StringExp *)e;
se->committed = 0;
}
elements->data[i] = (void *)e;
}
if (!t0)
t0 = Type::tvoid;
type = new TypeSArray(t0, new IntegerExp(elements->dim));
type = type->semantic(loc, sc);
return this;
}
int ArrayLiteralExp::checkSideEffect(int flag)
{ int f = 0;
for (size_t i = 0; i < elements->dim; i++)
{ Expression *e = (Expression *)elements->data[i];
f |= e->checkSideEffect(2);
}
if (flag == 0 && f == 0)
Expression::checkSideEffect(0);
return f;
}
int ArrayLiteralExp::isBool(int result)
{
size_t dim = elements ? elements->dim : 0;
return result ? (dim != 0) : (dim == 0);
}
#if DMDV2
int ArrayLiteralExp::canThrow()
{
return 1; // because it can fail allocating memory
}
#endif
void ArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writeByte('[');
argsToCBuffer(buf, elements, hgs);
buf->writeByte(']');
}
void ArrayLiteralExp::toMangleBuffer(OutBuffer *buf)
{
size_t dim = elements ? elements->dim : 0;
buf->printf("A%zu", dim);
for (size_t i = 0; i < dim; i++)
{ Expression *e = (Expression *)elements->data[i];
e->toMangleBuffer(buf);
}
}
/************************ AssocArrayLiteralExp ************************************/
// [ key0 : value0, key1 : value1, ... ]
AssocArrayLiteralExp::AssocArrayLiteralExp(Loc loc,
Expressions *keys, Expressions *values)
: Expression(loc, TOKassocarrayliteral, sizeof(AssocArrayLiteralExp))
{
assert(keys->dim == values->dim);
this->keys = keys;
this->values = values;
}
Expression *AssocArrayLiteralExp::syntaxCopy()
{
return new AssocArrayLiteralExp(loc,
arraySyntaxCopy(keys), arraySyntaxCopy(values));
}
Expression *AssocArrayLiteralExp::semantic(Scope *sc)
{ Expression *e;
#if LOGSEMANTIC
printf("AssocArrayLiteralExp::semantic('%s')\n", toChars());
#endif
// Run semantic() on each element
arrayExpressionSemantic(keys, sc);
arrayExpressionSemantic(values, sc);
expandTuples(keys);
expandTuples(values);
if (keys->dim != values->dim)
{
error("number of keys is %u, must match number of values %u", keys->dim, values->dim);
keys->setDim(0);
values->setDim(0);
}
Type *tkey = NULL;
Type *tvalue = NULL;
keys = arrayExpressionToCommonType(sc, keys, &tkey);
values = arrayExpressionToCommonType(sc, values, &tvalue);
type = new TypeAArray(tvalue, tkey);
type = type->semantic(loc, sc);
return this;
}
int AssocArrayLiteralExp::checkSideEffect(int flag)
{ int f = 0;
for (size_t i = 0; i < keys->dim; i++)
{ Expression *key = (Expression *)keys->data[i];
Expression *value = (Expression *)values->data[i];
f |= key->checkSideEffect(2);
f |= value->checkSideEffect(2);
}
if (flag == 0 && f == 0)
Expression::checkSideEffect(0);
return f;
}
int AssocArrayLiteralExp::isBool(int result)
{
size_t dim = keys->dim;
return result ? (dim != 0) : (dim == 0);
}
#if DMDV2
int AssocArrayLiteralExp::canThrow()
{
return 1;
}
#endif
void AssocArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writeByte('[');
for (size_t i = 0; i < keys->dim; i++)
{ Expression *key = (Expression *)keys->data[i];
Expression *value = (Expression *)values->data[i];
if (i)
buf->writeByte(',');
expToCBuffer(buf, hgs, key, PREC_assign);
buf->writeByte(':');
expToCBuffer(buf, hgs, value, PREC_assign);
}
buf->writeByte(']');
}
void AssocArrayLiteralExp::toMangleBuffer(OutBuffer *buf)
{
size_t dim = keys->dim;
buf->printf("A%zu", dim);
for (size_t i = 0; i < dim; i++)
{ Expression *key = (Expression *)keys->data[i];
Expression *value = (Expression *)values->data[i];
key->toMangleBuffer(buf);
value->toMangleBuffer(buf);
}
}
/************************ StructLiteralExp ************************************/
// sd( e1, e2, e3, ... )
StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements)
: Expression(loc, TOKstructliteral, sizeof(StructLiteralExp))
{
this->sd = sd;
this->elements = elements;
#if IN_DMD
this->sym = NULL;
#endif
this->soffset = 0;
this->fillHoles = 1;
}
Expression *StructLiteralExp::syntaxCopy()
{
return new StructLiteralExp(loc, sd, arraySyntaxCopy(elements));
}
Expression *StructLiteralExp::semantic(Scope *sc)
{ Expression *e;
#if LOGSEMANTIC
printf("StructLiteralExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
// Run semantic() on each element
for (size_t i = 0; i < elements->dim; i++)
{ e = (Expression *)elements->data[i];
if (!e)
continue;
e = e->semantic(sc);
elements->data[i] = (void *)e;
}
expandTuples(elements);
size_t offset = 0;
for (size_t i = 0; i < elements->dim; i++)
{ e = (Expression *)elements->data[i];
if (!e)
continue;
if (!e->type)
error("%s has no value", e->toChars());
e = resolveProperties(sc, e);
if (i >= sd->fields.dim)
{ error("more initializers than fields of %s", sd->toChars());
break;
}
Dsymbol *s = (Dsymbol *)sd->fields.data[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v);
if (v->offset < offset)
error("overlapping initialization for %s", v->toChars());
offset = v->offset + v->type->size();
Type *telem = v->type;
while (!e->implicitConvTo(telem) && telem->toBasetype()->ty == Tsarray)
{ /* Static array initialization, as in:
* T[3][5] = e;
*/
telem = telem->toBasetype()->nextOf();
}
e = e->implicitCastTo(sc, telem);
elements->data[i] = (void *)e;
}
/* Fill out remainder of elements[] with default initializers for fields[]
*/
for (size_t i = elements->dim; i < sd->fields.dim; i++)
{ Dsymbol *s = (Dsymbol *)sd->fields.data[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v);
if (v->offset < offset)
{ e = NULL;
sd->hasUnions = 1;
}
else
{
if (v->init)
{ e = v->init->toExpression();
if (!e)
{ error("cannot make expression out of initializer for %s", v->toChars());
e = new ErrorExp();
}
else if (v->scope)
{ // Do deferred semantic anaylsis
Initializer *i2 = v->init->syntaxCopy();
i2 = i2->semantic(v->scope, v->type);
e = i2->toExpression();
v->scope = NULL;
}
}
else
e = v->type->defaultInitLiteral(loc);
offset = v->offset + v->type->size();
}
elements->push(e);
}
type = sd->type;
return this;
}
/**************************************
* Gets expression at offset of type.
* Returns NULL if not found.
*/
Expression *StructLiteralExp::getField(Type *type, unsigned offset)
{
//printf("StructLiteralExp::getField(this = %s, type = %s, offset = %u)\n",
// /*toChars()*/"", type->toChars(), offset);
Expression *e = NULL;
int i = getFieldIndex(type, offset);
if (i != -1)
{
//printf("\ti = %d\n", i);
assert(i < elements->dim);
e = (Expression *)elements->data[i];
if (e)
{
//printf("e = %s, e->type = %s\n", e->toChars(), e->type->toChars());
/* If type is a static array, and e is an initializer for that array,
* then the field initializer should be an array literal of e.
*/
if (e->type != type && type->ty == Tsarray)
{ TypeSArray *tsa = (TypeSArray *)type;
uinteger_t length = tsa->dim->toInteger();
Expressions *z = new Expressions;
z->setDim(length);
for (int q = 0; q < length; ++q)
z->data[q] = e->copy();
e = new ArrayLiteralExp(loc, z);
e->type = type;
}
else
{
e = e->copy();
e->type = type;
}
}
}
return e;
}
/************************************
* Get index of field.
* Returns -1 if not found.
*/
int StructLiteralExp::getFieldIndex(Type *type, unsigned offset)
{
/* Find which field offset is by looking at the field offsets
*/
if (elements->dim)
{
for (size_t i = 0; i < sd->fields.dim; i++)
{
Dsymbol *s = (Dsymbol *)sd->fields.data[i];
VarDeclaration *v = s->isVarDeclaration();
assert(v);
if (offset == v->offset &&
type->size() == v->type->size())
{ Expression *e = (Expression *)elements->data[i];
if (e)
{
return i;
}
break;
}
}
}
return -1;
}
#if DMDV2
int StructLiteralExp::isLvalue()
{
return 1;
}
#endif
Expression *StructLiteralExp::toLvalue(Scope *sc, Expression *e)
{
return this;
}
int StructLiteralExp::checkSideEffect(int flag)
{ int f = 0;
for (size_t i = 0; i < elements->dim; i++)
{ Expression *e = (Expression *)elements->data[i];
if (!e)
continue;
f |= e->checkSideEffect(2);
}
if (flag == 0 && f == 0)
Expression::checkSideEffect(0);
return f;
}
#if DMDV2
int StructLiteralExp::canThrow()
{
return arrayExpressionCanThrow(elements);
}
#endif
void StructLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring(sd->toChars());
buf->writeByte('(');
argsToCBuffer(buf, elements, hgs);
buf->writeByte(')');
}
void StructLiteralExp::toMangleBuffer(OutBuffer *buf)
{
size_t dim = elements ? elements->dim : 0;
buf->printf("S%zu", dim);
for (size_t i = 0; i < dim; i++)
{ Expression *e = (Expression *)elements->data[i];
if (e)
e->toMangleBuffer(buf);
else
buf->writeByte('v'); // 'v' for void
}
}
/************************ TypeDotIdExp ************************************/
/* Things like:
* int.size
* foo.size
* (foo).size
* cast(foo).size
*/
Expression *typeDotIdExp(Loc loc, Type *type, Identifier *ident)
{
return new DotIdExp(loc, new TypeExp(loc, type), ident);
}
/************************************************************/
// Mainly just a placeholder
TypeExp::TypeExp(Loc loc, Type *type)
: Expression(loc, TOKtype, sizeof(TypeExp))
{
//printf("TypeExp::TypeExp(%s)\n", type->toChars());
this->type = type;
}
Expression *TypeExp::syntaxCopy()
{
//printf("TypeExp::syntaxCopy()\n");
return new TypeExp(loc, type->syntaxCopy());
}
Expression *TypeExp::semantic(Scope *sc)
{
//printf("TypeExp::semantic(%s)\n", type->toChars());
type = type->semantic(loc, sc);
return this;
}
void TypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
type->toCBuffer(buf, NULL, hgs);
}
/************************************************************/
// Mainly just a placeholder
ScopeExp::ScopeExp(Loc loc, ScopeDsymbol *pkg)
: Expression(loc, TOKimport, sizeof(ScopeExp))
{
//printf("ScopeExp::ScopeExp(pkg = '%s')\n", pkg->toChars());
//static int count; if (++count == 38) *(char*)0=0;
this->sds = pkg;
}
Expression *ScopeExp::syntaxCopy()
{
ScopeExp *se = new ScopeExp(loc, (ScopeDsymbol *)sds->syntaxCopy(NULL));
return se;
}
Expression *ScopeExp::semantic(Scope *sc)
{
TemplateInstance *ti;
ScopeDsymbol *sds2;
#if LOGSEMANTIC
printf("+ScopeExp::semantic('%s')\n", toChars());
#endif
Lagain:
ti = sds->isTemplateInstance();
if (ti && !global.errors)
{
if (!ti->semanticRun)
ti->semantic(sc);
if (ti->inst)
{
Dsymbol *s = ti->inst->toAlias();
sds2 = s->isScopeDsymbol();
if (!sds2)
{ Expression *e;
//printf("s = %s, '%s'\n", s->kind(), s->toChars());
if (ti->withsym)
{
// Same as wthis.s
e = new VarExp(loc, ti->withsym->withstate->wthis);
e = new DotVarExp(loc, e, s->isDeclaration());
}
else
e = new DsymbolExp(loc, s);
e = e->semantic(sc);
//printf("-1ScopeExp::semantic()\n");
return e;
}
if (sds2 != sds)
{
sds = sds2;
goto Lagain;
}
//printf("sds = %s, '%s'\n", sds->kind(), sds->toChars());
}
}
else
{
//printf("sds = %s, '%s'\n", sds->kind(), sds->toChars());
//printf("\tparent = '%s'\n", sds->parent->toChars());
sds->semantic(sc);
}
type = Type::tvoid;
//printf("-2ScopeExp::semantic() %s\n", toChars());
return this;
}
void ScopeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (sds->isTemplateInstance())
{
sds->toCBuffer(buf, hgs);
}
else
{
buf->writestring(sds->kind());
buf->writestring(" ");
buf->writestring(sds->toChars());
}
}
/********************** TemplateExp **************************************/
// Mainly just a placeholder
TemplateExp::TemplateExp(Loc loc, TemplateDeclaration *td)
: Expression(loc, TOKtemplate, sizeof(TemplateExp))
{
//printf("TemplateExp(): %s\n", td->toChars());
this->td = td;
}
void TemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring(td->toChars());
}
void TemplateExp::rvalue()
{
error("template %s has no value", toChars());
}
/********************** NewExp **************************************/
/* thisexp.new(newargs) newtype(arguments) */
NewExp::NewExp(Loc loc, Expression *thisexp, Expressions *newargs,
Type *newtype, Expressions *arguments)
: Expression(loc, TOKnew, sizeof(NewExp))
{
this->thisexp = thisexp;
this->newargs = newargs;
this->newtype = newtype;
this->arguments = arguments;
member = NULL;
allocator = NULL;
onstack = 0;
}
Expression *NewExp::syntaxCopy()
{
return new NewExp(loc,
thisexp ? thisexp->syntaxCopy() : NULL,
arraySyntaxCopy(newargs),
newtype->syntaxCopy(), arraySyntaxCopy(arguments));
}
Expression *NewExp::semantic(Scope *sc)
{ int i;
Type *tb;
ClassDeclaration *cdthis = NULL;
#if LOGSEMANTIC
printf("NewExp::semantic() %s\n", toChars());
if (thisexp)
printf("\tthisexp = %s\n", thisexp->toChars());
printf("\tnewtype: %s\n", newtype->toChars());
#endif
if (type) // if semantic() already run
return this;
Lagain:
if (thisexp)
{ thisexp = thisexp->semantic(sc);
cdthis = thisexp->type->isClassHandle();
if (cdthis)
{
sc = sc->push(cdthis);
type = newtype->semantic(loc, sc);
sc = sc->pop();
}
else
{
error("'this' for nested class must be a class type, not %s", thisexp->type->toChars());
type = newtype->semantic(loc, sc);
}
}
else
type = newtype->semantic(loc, sc);
newtype = type; // in case type gets cast to something else
tb = type->toBasetype();
//printf("tb: %s, deco = %s\n", tb->toChars(), tb->deco);
arrayExpressionSemantic(newargs, sc);
preFunctionParameters(loc, sc, newargs);
arrayExpressionSemantic(arguments, sc);
preFunctionParameters(loc, sc, arguments);
if (thisexp && tb->ty != Tclass)
error("e.new is only for allocating nested classes, not %s", tb->toChars());
if (tb->ty == Tclass)
{ TypeFunction *tf;
TypeClass *tc = (TypeClass *)(tb);
ClassDeclaration *cd = tc->sym->isClassDeclaration();
if (cd->isInterfaceDeclaration())
error("cannot create instance of interface %s", cd->toChars());
else if (cd->isAbstract())
{ error("cannot create instance of abstract class %s", cd->toChars());
for (int i = 0; i < cd->vtbl.dim; i++)
{ FuncDeclaration *fd = ((Dsymbol *)cd->vtbl.data[i])->isFuncDeclaration();
if (fd && fd->isAbstract())
error("function %s is abstract", fd->toChars());
}
}
checkDeprecated(sc, cd);
if (cd->isNested())
{ /* We need a 'this' pointer for the nested class.
* Ensure we have the right one.
*/
Dsymbol *s = cd->toParent2();
ClassDeclaration *cdn = s->isClassDeclaration();
FuncDeclaration *fdn = s->isFuncDeclaration();
//printf("cd isNested, cdn = %s\n", cdn ? cdn->toChars() : "null");
if (cdn)
{
if (!cdthis)
{
// Supply an implicit 'this' and try again
thisexp = new ThisExp(loc);
for (Dsymbol *sp = sc->parent; 1; sp = sp->parent)
{ if (!sp)
{
error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars());
break;
}
ClassDeclaration *cdp = sp->isClassDeclaration();
if (!cdp)
continue;
if (cdp == cdn || cdn->isBaseOf(cdp, NULL))
break;
// Add a '.outer' and try again
thisexp = new DotIdExp(loc, thisexp, Id::outer);
}
if (!global.errors)
goto Lagain;
}
if (cdthis)
{
//printf("cdthis = %s\n", cdthis->toChars());
if (cdthis != cdn && !cdn->isBaseOf(cdthis, NULL))
error("'this' for nested class must be of type %s, not %s", cdn->toChars(), thisexp->type->toChars());
}
#if 0
else
{
for (Dsymbol *sf = sc->func; 1; sf= sf->toParent2()->isFuncDeclaration())
{
if (!sf)
{
error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars());
break;
}
printf("sf = %s\n", sf->toChars());
AggregateDeclaration *ad = sf->isThis();
if (ad && (ad == cdn || cdn->isBaseOf(ad->isClassDeclaration(), NULL)))
break;
}
}
#endif
}
else if (thisexp)
error("e.new is only for allocating nested classes");
else if (fdn)
{
// make sure the parent context fdn of cd is reachable from sc
for (Dsymbol *sp = sc->parent; 1; sp = sp->parent)
{
if (fdn == sp)
break;
FuncDeclaration *fsp = sp ? sp->isFuncDeclaration() : NULL;
if (!sp || (fsp && fsp->isStatic()))
{
error("outer function context of %s is needed to 'new' nested class %s", fdn->toPrettyChars(), cd->toPrettyChars());
break;
}
}
}
}
else if (thisexp)
error("e.new is only for allocating nested classes");
FuncDeclaration *f = cd->ctor;
if (f)
{
assert(f);
f = f->overloadResolve(loc, NULL, arguments, sc->module);
checkDeprecated(sc, f);
member = f->isCtorDeclaration();
assert(member);
cd->accessCheck(loc, sc, member);
tf = (TypeFunction *)f->type;
type = tf->next;
if (!arguments)
arguments = new Expressions();
functionParameters(loc, sc, tf, arguments);
}
else
{
if (arguments && arguments->dim)
error("no constructor for %s", cd->toChars());
}
if (cd->aggNew)
{
// Prepend the size argument to newargs[]
Expression *e = new IntegerExp(loc, cd->size(loc), Type::tsize_t);
if (!newargs)
newargs = new Expressions();
newargs->shift(e);
f = cd->aggNew->overloadResolve(loc, NULL, newargs, sc->module);
allocator = f->isNewDeclaration();
assert(allocator);
tf = (TypeFunction *)f->type;
functionParameters(loc, sc, tf, newargs);
}
else
{
if (newargs && newargs->dim)
error("no allocator for %s", cd->toChars());
}
}
else if (tb->ty == Tstruct)
{
TypeStruct *ts = (TypeStruct *)tb;
StructDeclaration *sd = ts->sym;
FuncDeclaration *f = sd->aggNew;
TypeFunction *tf;
if (arguments && arguments->dim)
error("no constructor for %s", type->toChars());
if (f)
{
Expression *e;
// Prepend the uint size argument to newargs[]
e = new IntegerExp(loc, sd->size(loc), Type::tuns32);
if (!newargs)
newargs = new Expressions();
newargs->shift(e);
f = f->overloadResolve(loc, NULL, newargs, sc->module);
allocator = f->isNewDeclaration();
assert(allocator);
tf = (TypeFunction *)f->type;
functionParameters(loc, sc, tf, newargs);
e = new VarExp(loc, f);
e = new CallExp(loc, e, newargs);
e = e->semantic(sc);
e->type = type->pointerTo();
return e;
}
type = type->pointerTo();
}
else if (tb->ty == Tarray && (arguments && arguments->dim))
{
for (size_t i = 0; i < arguments->dim; i++)
{
if (tb->ty != Tarray)
{ error("too many arguments for array");
arguments->dim = i;
break;
}
Expression *arg = (Expression *)arguments->data[i];
arg = resolveProperties(sc, arg);
arg = arg->implicitCastTo(sc, Type::tsize_t);
if (arg->op == TOKint64 && (long long)arg->toInteger() < 0)
error("negative array index %s", arg->toChars());
arguments->data[i] = (void *) arg;
tb = ((TypeDArray *)tb)->next->toBasetype();
}
}
else if (tb->isscalar())
{
if (arguments && arguments->dim)
error("no constructor for %s", type->toChars());
type = type->pointerTo();
}
else
{
error("new can only create structs, dynamic arrays or class objects, not %s's", type->toChars());
type = type->pointerTo();
}
//printf("NewExp: '%s'\n", toChars());
//printf("NewExp:type '%s'\n", type->toChars());
return this;
}
int NewExp::checkSideEffect(int flag)
{
return 1;
}
#if DMDV2
int NewExp::canThrow()
{
return 1;
}
#endif
void NewExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{ int i;
if (thisexp)
{ expToCBuffer(buf, hgs, thisexp, PREC_primary);
buf->writeByte('.');
}
buf->writestring("new ");
if (newargs && newargs->dim)
{
buf->writeByte('(');
argsToCBuffer(buf, newargs, hgs);
buf->writeByte(')');
}
newtype->toCBuffer(buf, NULL, hgs);
if (arguments && arguments->dim)
{
buf->writeByte('(');
argsToCBuffer(buf, arguments, hgs);
buf->writeByte(')');
}
}
/********************** NewAnonClassExp **************************************/
NewAnonClassExp::NewAnonClassExp(Loc loc, Expression *thisexp,
Expressions *newargs, ClassDeclaration *cd, Expressions *arguments)
: Expression(loc, TOKnewanonclass, sizeof(NewAnonClassExp))
{
this->thisexp = thisexp;
this->newargs = newargs;
this->cd = cd;
this->arguments = arguments;
}
Expression *NewAnonClassExp::syntaxCopy()
{
return new NewAnonClassExp(loc,
thisexp ? thisexp->syntaxCopy() : NULL,
arraySyntaxCopy(newargs),
(ClassDeclaration *)cd->syntaxCopy(NULL),
arraySyntaxCopy(arguments));
}
Expression *NewAnonClassExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("NewAnonClassExp::semantic() %s\n", toChars());
//printf("thisexp = %p\n", thisexp);
//printf("type: %s\n", type->toChars());
#endif
Expression *d = new DeclarationExp(loc, cd);
d = d->semantic(sc);
Expression *n = new NewExp(loc, thisexp, newargs, cd->type, arguments);
Expression *c = new CommaExp(loc, d, n);
return c->semantic(sc);
}
int NewAnonClassExp::checkSideEffect(int flag)
{
return 1;
}
#if DMDV2
int NewAnonClassExp::canThrow()
{
return 1;
}
#endif
void NewAnonClassExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{ int i;
if (thisexp)
{ expToCBuffer(buf, hgs, thisexp, PREC_primary);
buf->writeByte('.');
}
buf->writestring("new");
if (newargs && newargs->dim)
{
buf->writeByte('(');
argsToCBuffer(buf, newargs, hgs);
buf->writeByte(')');
}
buf->writestring(" class ");
if (arguments && arguments->dim)
{
buf->writeByte('(');
argsToCBuffer(buf, arguments, hgs);
buf->writeByte(')');
}
//buf->writestring(" { }");
if (cd)
{
cd->toCBuffer(buf, hgs);
}
}
/********************** SymbolExp **************************************/
#if DMDV2
SymbolExp::SymbolExp(Loc loc, enum TOK op, int size, Declaration *var, int hasOverloads)
: Expression(loc, op, size)
{
assert(var);
this->var = var;
this->hasOverloads = hasOverloads;
}
#endif
/********************** SymOffExp **************************************/
SymOffExp::SymOffExp(Loc loc, Declaration *var, unsigned offset)
: Expression(loc, TOKsymoff, sizeof(SymOffExp))
{
assert(var);
this->var = var;
this->offset = offset;
m = NULL;
VarDeclaration *v = var->isVarDeclaration();
if (v && v->needThis())
error("need 'this' for address of %s", v->toChars());
}
Expression *SymOffExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("SymOffExp::semantic('%s')\n", toChars());
#endif
//var->semantic(sc);
m = sc->module;
if (!type)
type = var->type->pointerTo();
VarDeclaration *v = var->isVarDeclaration();
if (v)
v->checkNestedReference(sc, loc);
return this;
}
int SymOffExp::isBool(int result)
{
return result ? TRUE : FALSE;
}
void SymOffExp::checkEscape()
{
VarDeclaration *v = var->isVarDeclaration();
if (v)
{
if (!v->isDataseg() && !(v->storage_class & (STCref | STCout)))
{ /* BUG: This should be allowed:
* void foo()
* { int a;
* int* bar() { return &a; }
* }
*/
error("escaping reference to local %s", v->toChars());
}
}
}
void SymOffExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (offset)
buf->printf("(& %s+%u)", var->toChars(), offset);
else
buf->printf("& %s", var->toChars());
}
/******************************** VarExp **************************/
VarExp::VarExp(Loc loc, Declaration *var)
: Expression(loc, TOKvar, sizeof(VarExp))
{
//printf("VarExp(this = %p, '%s')\n", this, var->toChars());
this->var = var;
this->type = var->type;
}
int VarExp::equals(Object *o)
{ VarExp *ne;
if (this == o ||
(((Expression *)o)->op == TOKvar &&
((ne = (VarExp *)o), type->equals(ne->type)) &&
var == ne->var))
return 1;
return 0;
}
Expression *VarExp::semantic(Scope *sc)
{ FuncLiteralDeclaration *fd;
#if LOGSEMANTIC
printf("VarExp::semantic(%s)\n", toChars());
#endif
if (!type)
{ type = var->type;
#if 0
if (var->storage_class & STClazy)
{
TypeFunction *tf = new TypeFunction(NULL, type, 0, LINKd);
type = new TypeDelegate(tf);
type = type->semantic(loc, sc);
}
#endif
}
if (type && !type->deco)
type = type->semantic(loc, sc);
/* Fix for 1161 doesn't work because it causes protection
* problems when instantiating imported templates passing private
* variables as alias template parameters.
*/
//accessCheck(loc, sc, NULL, var);
VarDeclaration *v = var->isVarDeclaration();
if (v)
{
if (v->isSameAsInitializer() && type->toBasetype()->ty != Tsarray && v->init)
{
ExpInitializer *ei = v->init->isExpInitializer();
if (ei && ei->exp->type)
{
//ei->exp->implicitCastTo(sc, type)->print();
return ei->exp->implicitCastTo(sc, type);
}
}
v->checkNestedReference(sc, loc);
#if DMDV2
if (sc->func && sc->func->isPure() && !sc->intypeof)
{
if (v->isDataseg() && !v->isImmutable())
error("pure function '%s' cannot access mutable static data '%s'", sc->func->toChars(), v->toChars());
}
#endif
}
#if 0
else if ((fd = var->isFuncLiteralDeclaration()) != NULL)
{ Expression *e;
e = new FuncExp(loc, fd);
e->type = type;
return e;
}
#endif
return this;
}
char *VarExp::toChars()
{
return var->toChars();
}
void VarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring(var->toChars());
}
void VarExp::checkEscape()
{
VarDeclaration *v = var->isVarDeclaration();
if (v)
{ Type *tb = v->type->toBasetype();
// if reference type
if (tb->ty == Tarray || tb->ty == Tsarray || tb->ty == Tclass)
{
if (v->isScope() && !v->noscope)
error("escaping reference to auto local %s", v->toChars());
else if (v->storage_class & STCvariadic)
error("escaping reference to variadic parameter %s", v->toChars());
}
}
}
void VarExp::checkEscapeRef()
{
VarDeclaration *v = var->isVarDeclaration();
if (v)
{
if (!v->isDataseg() && !(v->storage_class & (STCref | STCout)))
error("escaping reference to local variable %s", v->toChars());
}
}
#if DMDV2
int VarExp::isLvalue()
{
if (var->storage_class & STClazy)
return 0;
return 1;
}
#endif
Expression *VarExp::toLvalue(Scope *sc, Expression *e)
{
#if 0
tym = tybasic(e1->ET->Tty);
if (!(tyscalar(tym) ||
tym == TYstruct ||
tym == TYarray && e->Eoper == TOKaddr))
synerr(EM_lvalue); // lvalue expected
#endif
if (var->storage_class & STClazy)
error("lazy variables cannot be lvalues");
return this;
}
Expression *VarExp::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("VarExp::modifiableLvalue('%s')\n", var->toChars());
if (sc->incontract && var->isParameter())
error("cannot modify parameter '%s' in contract", var->toChars());
if (type && type->toBasetype()->ty == Tsarray)
error("cannot change reference to static array '%s'", var->toChars());
VarDeclaration *v = var->isVarDeclaration();
if (v && v->canassign == 0 &&
(var->isConst() || (global.params.Dversion > 1 && var->isFinal())))
error("cannot modify final variable '%s'", var->toChars());
if (var->isCtorinit())
{ // It's only modifiable if inside the right constructor
Dsymbol *s = sc->func;
while (1)
{
FuncDeclaration *fd = NULL;
if (s)
fd = s->isFuncDeclaration();
if (fd &&
((fd->isCtorDeclaration() && var->storage_class & STCfield) ||
(fd->isStaticCtorDeclaration() && !(var->storage_class & STCfield))) &&
fd->toParent() == var->toParent()
)
{
VarDeclaration *v = var->isVarDeclaration();
assert(v);
v->ctorinit = 1;
//printf("setting ctorinit\n");
}
else
{
if (s)
{ s = s->toParent2();
continue;
}
else
{
const char *p = var->isStatic() ? "static " : "";
error("can only initialize %sconst %s inside %sconstructor",
p, var->toChars(), p);
}
}
break;
}
}
// See if this expression is a modifiable lvalue (i.e. not const)
return toLvalue(sc, e);
}
/******************************** OverExp **************************/
#if DMDV2
OverExp::OverExp(OverloadSet *s)
: Expression(loc, TOKoverloadset, sizeof(OverExp))
{
//printf("OverExp(this = %p, '%s')\n", this, var->toChars());
vars = s;
type = Type::tvoid;
}
int OverExp::isLvalue()
{
return 1;
}
Expression *OverExp::toLvalue(Scope *sc, Expression *e)
{
return this;
}
#endif
/******************************** TupleExp **************************/
TupleExp::TupleExp(Loc loc, Expressions *exps)
: Expression(loc, TOKtuple, sizeof(TupleExp))
{
//printf("TupleExp(this = %p)\n", this);
this->exps = exps;
this->type = NULL;
}
TupleExp::TupleExp(Loc loc, TupleDeclaration *tup)
: Expression(loc, TOKtuple, sizeof(TupleExp))
{
exps = new Expressions();
type = NULL;
exps->reserve(tup->objects->dim);
for (size_t i = 0; i < tup->objects->dim; i++)
{ Object *o = (Object *)tup->objects->data[i];
if (o->dyncast() == DYNCAST_EXPRESSION)
{
Expression *e = (Expression *)o;
e = e->syntaxCopy();
exps->push(e);
}
else if (o->dyncast() == DYNCAST_DSYMBOL)
{
Dsymbol *s = (Dsymbol *)o;
Expression *e = new DsymbolExp(loc, s);
exps->push(e);
}
else if (o->dyncast() == DYNCAST_TYPE)
{
Type *t = (Type *)o;
Expression *e = new TypeExp(loc, t);
exps->push(e);
}
else
{
error("%s is not an expression", o->toChars());
}
}
}
int TupleExp::equals(Object *o)
{ TupleExp *ne;
if (this == o)
return 1;
if (((Expression *)o)->op == TOKtuple)
{
TupleExp *te = (TupleExp *)o;
if (exps->dim != te->exps->dim)
return 0;
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e1 = (Expression *)exps->data[i];
Expression *e2 = (Expression *)te->exps->data[i];
if (!e1->equals(e2))
return 0;
}
return 1;
}
return 0;
}
Expression *TupleExp::syntaxCopy()
{
return new TupleExp(loc, arraySyntaxCopy(exps));
}
Expression *TupleExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("+TupleExp::semantic(%s)\n", toChars());
#endif
if (type)
return this;
// Run semantic() on each argument
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
e = e->semantic(sc);
if (!e->type)
{ error("%s has no value", e->toChars());
e->type = Type::terror;
}
exps->data[i] = (void *)e;
}
expandTuples(exps);
if (0 && exps->dim == 1)
{
return (Expression *)exps->data[0];
}
type = new TypeTuple(exps);
type = type->semantic(loc, sc);
//printf("-TupleExp::semantic(%s)\n", toChars());
return this;
}
void TupleExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("tuple(");
argsToCBuffer(buf, exps, hgs);
buf->writeByte(')');
}
int TupleExp::checkSideEffect(int flag)
{ int f = 0;
for (int i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
f |= e->checkSideEffect(2);
}
if (flag == 0 && f == 0)
Expression::checkSideEffect(0);
return f;
}
#if DMDV2
int TupleExp::canThrow()
{
return arrayExpressionCanThrow(exps);
}
#endif
void TupleExp::checkEscape()
{
for (size_t i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)exps->data[i];
e->checkEscape();
}
}
/******************************** FuncExp *********************************/
FuncExp::FuncExp(Loc loc, FuncLiteralDeclaration *fd)
: Expression(loc, TOKfunction, sizeof(FuncExp))
{
this->fd = fd;
}
Expression *FuncExp::syntaxCopy()
{
return new FuncExp(loc, (FuncLiteralDeclaration *)fd->syntaxCopy(NULL));
}
Expression *FuncExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("FuncExp::semantic(%s)\n", toChars());
#endif
if (!type)
{
fd->semantic(sc);
fd->parent = sc->parent;
if (global.errors)
{
}
else
{
fd->semantic2(sc);
if (!global.errors ||
// need to infer return type
(fd->type && fd->type->ty == Tfunction && !fd->type->nextOf()))
{
fd->semantic3(sc);
if (!global.errors && global.params.useInline)
fd->inlineScan();
}
}
// need to infer return type
if (global.errors && fd->type && fd->type->ty == Tfunction && !fd->type->nextOf())
((TypeFunction *)fd->type)->next = Type::terror;
// Type is a "delegate to" or "pointer to" the function literal
if (fd->isNested())
{
type = new TypeDelegate(fd->type);
type = type->semantic(loc, sc);
}
else
{
type = fd->type->pointerTo();
}
}
return this;
}
char *FuncExp::toChars()
{
return fd->toChars();
}
void FuncExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
fd->toCBuffer(buf, hgs);
//buf->writestring(fd->toChars());
}
/******************************** DeclarationExp **************************/
DeclarationExp::DeclarationExp(Loc loc, Dsymbol *declaration)
: Expression(loc, TOKdeclaration, sizeof(DeclarationExp))
{
this->declaration = declaration;
}
Expression *DeclarationExp::syntaxCopy()
{
return new DeclarationExp(loc, declaration->syntaxCopy(NULL));
}
Expression *DeclarationExp::semantic(Scope *sc)
{
if (type)
return this;
#if LOGSEMANTIC
printf("DeclarationExp::semantic() %s\n", toChars());
#endif
/* This is here to support extern(linkage) declaration,
* where the extern(linkage) winds up being an AttribDeclaration
* wrapper.
*/
Dsymbol *s = declaration;
AttribDeclaration *ad = declaration->isAttribDeclaration();
if (ad)
{
if (ad->decl && ad->decl->dim == 1)
s = (Dsymbol *)ad->decl->data[0];
}
if (s->isVarDeclaration())
{ // Do semantic() on initializer first, so:
// int a = a;
// will be illegal.
declaration->semantic(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))
error("declaration %s is already defined", s->toPrettyChars());
else if (sc->func)
{ VarDeclaration *v = s->isVarDeclaration();
if ( (s->isFuncDeclaration() || s->isTypedefDeclaration() ||
s->isAggregateDeclaration() || s->isEnumDeclaration() ||
s->isInterfaceDeclaration()) &&
!sc->func->localsymtab->insert(s))
{
error("declaration %s is already defined in another scope in %s",
s->toPrettyChars(), sc->func->toChars());
}
else if (!global.params.useDeprecated)
{ // Disallow shadowing
for (Scope *scx = sc->enclosing; scx && scx->func == sc->func; scx = scx->enclosing)
{ Dsymbol *s2;
if (scx->scopesym && scx->scopesym->symtab &&
(s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL &&
s != s2)
{
error("shadowing declaration %s is deprecated", s->toPrettyChars());
}
}
}
}
}
if (!s->isVarDeclaration())
{
Scope *sc2 = sc;
if (sc2->stc & (STCpure | STCnothrow))
sc2 = sc->push();
sc2->stc &= ~(STCpure | STCnothrow);
declaration->semantic(sc2);
if (sc2 != sc)
sc2->pop();
s->parent = sc->parent;
}
if (!global.errors)
{
declaration->semantic2(sc);
if (!global.errors)
{
declaration->semantic3(sc);
if (!global.errors && global.params.useInline)
declaration->inlineScan();
}
}
type = Type::tvoid;
return this;
}
int DeclarationExp::checkSideEffect(int flag)
{
return 1;
}
#if DMDV2
int DeclarationExp::canThrow()
{
VarDeclaration *v = declaration->isVarDeclaration();
if (v && v->init)
{ ExpInitializer *ie = v->init->isExpInitializer();
return ie && ie->exp->canThrow();
}
return 0;
}
#endif
void DeclarationExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
declaration->toCBuffer(buf, hgs);
}
/************************ TypeidExp ************************************/
/*
* typeid(int)
*/
TypeidExp::TypeidExp(Loc loc, Type *typeidType)
: Expression(loc, TOKtypeid, sizeof(TypeidExp))
{
this->typeidType = typeidType;
}
Expression *TypeidExp::syntaxCopy()
{
return new TypeidExp(loc, typeidType->syntaxCopy());
}
Expression *TypeidExp::semantic(Scope *sc)
{ Expression *e;
#if LOGSEMANTIC
printf("TypeidExp::semantic()\n");
#endif
typeidType = typeidType->semantic(loc, sc);
e = typeidType->getTypeInfo(sc);
if (e->loc.linnum == 0)
e->loc = loc; // so there's at least some line number info
return e;
}
void TypeidExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("typeid(");
typeidType->toCBuffer(buf, NULL, hgs);
buf->writeByte(')');
}
/************************ TraitsExp ************************************/
#if DMDV2
/*
* __traits(identifier, args...)
*/
TraitsExp::TraitsExp(Loc loc, Identifier *ident, Objects *args)
: Expression(loc, TOKtraits, sizeof(TraitsExp))
{
this->ident = ident;
this->args = args;
}
Expression *TraitsExp::syntaxCopy()
{
return new TraitsExp(loc, ident, TemplateInstance::arraySyntaxCopy(args));
}
void TraitsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("__traits(");
buf->writestring(ident->toChars());
if (args)
{
for (int i = 0; i < args->dim; i++)
{
buf->writeByte(',');
Object *oarg = (Object *)args->data[i];
ObjectToCBuffer(buf, hgs, oarg);
}
}
buf->writeByte(')');
}
#endif
/************************************************************/
HaltExp::HaltExp(Loc loc)
: Expression(loc, TOKhalt, sizeof(HaltExp))
{
}
Expression *HaltExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("HaltExp::semantic()\n");
#endif
type = Type::tvoid;
return this;
}
int HaltExp::checkSideEffect(int flag)
{
return 1;
}
void HaltExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("halt");
}
/************************************************************/
IsExp::IsExp(Loc loc, Type *targ, Identifier *id, enum TOK tok,
Type *tspec, enum TOK tok2)
: Expression(loc, TOKis, sizeof(IsExp))
{
this->targ = targ;
this->id = id;
this->tok = tok;
this->tspec = tspec;
this->tok2 = tok2;
}
Expression *IsExp::syntaxCopy()
{
return new IsExp(loc,
targ->syntaxCopy(),
id,
tok,
tspec ? tspec->syntaxCopy() : NULL,
tok2);
}
Expression *IsExp::semantic(Scope *sc)
{ Type *tded;
/* is(targ id tok tspec)
* is(targ id == tok2)
*/
//printf("IsExp::semantic(%s)\n", toChars());
if (id && !(sc->flags & SCOPEstaticif))
error("can only declare type aliases within static if conditionals");
unsigned errors_save = global.errors;
global.errors = 0;
global.gag++; // suppress printing of error messages
targ = targ->semantic(loc, sc);
global.gag--;
unsigned gerrors = global.errors;
global.errors = errors_save;
if (gerrors) // if any errors happened
{ // then condition is false
goto Lno;
}
else if (tok2 != TOKreserved)
{
switch (tok2)
{
case TOKtypedef:
if (targ->ty != Ttypedef)
goto Lno;
tded = ((TypeTypedef *)targ)->sym->basetype;
break;
case TOKstruct:
if (targ->ty != Tstruct)
goto Lno;
if (((TypeStruct *)targ)->sym->isUnionDeclaration())
goto Lno;
tded = targ;
break;
case TOKunion:
if (targ->ty != Tstruct)
goto Lno;
if (!((TypeStruct *)targ)->sym->isUnionDeclaration())
goto Lno;
tded = targ;
break;
case TOKclass:
if (targ->ty != Tclass)
goto Lno;
if (((TypeClass *)targ)->sym->isInterfaceDeclaration())
goto Lno;
tded = targ;
break;
case TOKinterface:
if (targ->ty != Tclass)
goto Lno;
if (!((TypeClass *)targ)->sym->isInterfaceDeclaration())
goto Lno;
tded = targ;
break;
#if DMDV2
case TOKconst:
if (!targ->isConst())
goto Lno;
tded = targ;
break;
case TOKinvariant:
case TOKimmutable:
if (!targ->isImmutable())
goto Lno;
tded = targ;
break;
case TOKshared:
if (!targ->isShared())
goto Lno;
tded = targ;
break;
#endif
case TOKsuper:
// If class or interface, get the base class and interfaces
if (targ->ty != Tclass)
goto Lno;
else
{ ClassDeclaration *cd = ((TypeClass *)targ)->sym;
Parameters *args = new Parameters;
args->reserve(cd->baseclasses->dim);
for (size_t i = 0; i < cd->baseclasses->dim; i++)
{ BaseClass *b = (BaseClass *)cd->baseclasses->data[i];
args->push(new Parameter(STCin, b->type, NULL, NULL));
}
tded = new TypeTuple(args);
}
break;
case TOKenum:
if (targ->ty != Tenum)
goto Lno;
tded = ((TypeEnum *)targ)->sym->memtype;
break;
case TOKdelegate:
if (targ->ty != Tdelegate)
goto Lno;
tded = ((TypeDelegate *)targ)->next; // the underlying function type
break;
case TOKfunction:
{
if (targ->ty != Tfunction)
goto Lno;
tded = targ;
/* Generate tuple from function parameter types.
*/
assert(tded->ty == Tfunction);
Parameters *params = ((TypeFunction *)tded)->parameters;
size_t dim = Parameter::dim(params);
Parameters *args = new Parameters;
args->reserve(dim);
for (size_t i = 0; i < dim; i++)
{ Parameter *arg = Parameter::getNth(params, i);
assert(arg && arg->type);
args->push(new Parameter(arg->storageClass, arg->type, NULL, NULL));
}
tded = new TypeTuple(args);
break;
}
case TOKreturn:
/* Get the 'return type' for the function,
* delegate, or pointer to function.
*/
if (targ->ty == Tfunction)
tded = ((TypeFunction *)targ)->next;
else if (targ->ty == Tdelegate)
tded = targ->next->next;
else if (targ->ty == Tpointer && targ->next->ty == Tfunction)
tded = targ->next->next;
else
goto Lno;
break;
default:
assert(0);
}
goto Lyes;
}
else if (id && tspec)
{
/* Evaluate to TRUE if targ matches tspec.
* If TRUE, declare id as an alias for the specialized type.
*/
MATCH m;
TemplateTypeParameter tp(loc, id, NULL, NULL);
TemplateParameters parameters;
parameters.setDim(1);
parameters.data[0] = (void *)&tp;
Objects dedtypes;
dedtypes.setDim(1);
dedtypes.data[0] = NULL;
m = targ->deduceType(NULL, tspec, &parameters, &dedtypes);
if (m == MATCHnomatch ||
(m != MATCHexact && tok == TOKequal))
{
goto Lno;
}
else
{
assert(dedtypes.dim == 1);
tded = (Type *)dedtypes.data[0];
if (!tded)
tded = targ;
#if DMDV2
Objects tiargs;
tiargs.setDim(1);
tiargs.data[0] = (void *)targ;
/* Declare trailing parameters
*/
for (int i = 1; i < parameters->dim; i++)
{ TemplateParameter *tp = (TemplateParameter *)parameters->data[i];
Declaration *s = NULL;
m = tp->matchArg(sc, &tiargs, i, parameters, &dedtypes, &s);
if (m == MATCHnomatch)
goto Lno;
s->semantic(sc);
#if 0
Object *o = (Object *)dedtypes.data[i];
Dsymbol *s = TemplateDeclaration::declareParameter(loc, sc, tp, o);
#endif
if (sc->sd)
s->addMember(sc, sc->sd, 1);
else if (!sc->insert(s))
error("declaration %s is already defined", s->toChars());
}
#endif
goto Lyes;
}
}
else if (id)
{
/* Declare id as an alias for type targ. Evaluate to TRUE
*/
tded = targ;
goto Lyes;
}
else if (tspec)
{
/* Evaluate to TRUE if targ matches tspec
* is(targ == tspec)
* is(targ : tspec)
*/
tspec = tspec->semantic(loc, sc);
//printf("targ = %s\n", targ->toChars());
//printf("tspec = %s\n", tspec->toChars());
if (tok == TOKcolon)
{ if (targ->implicitConvTo(tspec))
goto Lyes;
else
goto Lno;
}
else /* == */
{ if (targ->equals(tspec))
goto Lyes;
else
goto Lno;
}
}
Lyes:
if (id)
{
Dsymbol *s = new AliasDeclaration(loc, id, tded);
s->semantic(sc);
sc->insert(s);
if (sc->sd)
s->addMember(sc, sc->sd, 1);
}
return new IntegerExp(1);
Lno:
return new IntegerExp(0);
}
void IsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("is(");
targ->toCBuffer(buf, id, hgs);
if (tok2 != TOKreserved)
{
buf->printf(" %s %s", Token::toChars(tok), Token::toChars(tok2));
}
else if (tspec)
{
if (tok == TOKcolon)
buf->writestring(" : ");
else
buf->writestring(" == ");
tspec->toCBuffer(buf, NULL, hgs);
}
#if DMDV2
if (parameters)
{ // First parameter is already output, so start with second
for (int i = 1; i < parameters->dim; i++)
{
buf->writeByte(',');
TemplateParameter *tp = (TemplateParameter *)parameters->data[i];
tp->toCBuffer(buf, hgs);
}
}
#endif
buf->writeByte(')');
}
/************************************************************/
UnaExp::UnaExp(Loc loc, enum TOK op, int size, Expression *e1)
: Expression(loc, op, size)
{
this->e1 = e1;
}
Expression *UnaExp::syntaxCopy()
{ UnaExp *e;
e = (UnaExp *)copy();
e->type = NULL;
e->e1 = e->e1->syntaxCopy();
return e;
}
Expression *UnaExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("UnaExp::semantic('%s')\n", toChars());
#endif
e1 = e1->semantic(sc);
// if (!e1->type)
// error("%s has no value", e1->toChars());
return this;
}
#if DMDV2
int UnaExp::canThrow()
{
return e1->canThrow();
}
#endif
void UnaExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring(Token::toChars(op));
expToCBuffer(buf, hgs, e1, precedence[op]);
}
/************************************************************/
BinExp::BinExp(Loc loc, enum TOK op, int size, Expression *e1, Expression *e2)
: Expression(loc, op, size)
{
this->e1 = e1;
this->e2 = e2;
}
Expression *BinExp::syntaxCopy()
{ BinExp *e;
e = (BinExp *)copy();
e->type = NULL;
e->e1 = e->e1->syntaxCopy();
e->e2 = e->e2->syntaxCopy();
return e;
}
Expression *BinExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("BinExp::semantic('%s')\n", toChars());
#endif
e1 = e1->semantic(sc);
if (!e1->type &&
!(op == TOKassign && e1->op == TOKdottd)) // a.template = e2
{
error("%s has no value", e1->toChars());
e1 = new ErrorExp();
}
e2 = e2->semantic(sc);
if (!e2->type)
{
error("%s has no value", e2->toChars());
e2 = new ErrorExp();
}
if (e1->op == TOKerror || e2->op == TOKerror)
return new ErrorExp();
return this;
}
Expression *BinExp::semanticp(Scope *sc)
{
BinExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e2 = resolveProperties(sc, e2);
return this;
}
/***************************
* Common semantic routine for some xxxAssignExp's.
*/
Expression *BinExp::commonSemanticAssign(Scope *sc)
{ Expression *e;
if (!type)
{
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
if (e1->op == TOKslice)
{ // T[] op= ...
e = typeCombine(sc);
if (e->op == TOKerror)
return e;
type = e1->type;
return arrayOp(sc);
}
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
type = e1->type;
if (type->toBasetype()->ty == Tbool)
{
error("operator not allowed on bool expression %s", toChars());
}
typeCombine(sc);
e1->checkArithmetic();
e2->checkArithmetic();
if (op == TOKmodass && e2->type->iscomplex())
{ error("cannot perform modulo complex arithmetic");
return new ErrorExp();
}
}
return this;
}
Expression *BinExp::commonSemanticAssignIntegral(Scope *sc)
{ Expression *e;
if (!type)
{
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
if (e1->op == TOKslice)
{ // T[] op= ...
e = typeCombine(sc);
if (e->op == TOKerror)
return e;
type = e1->type;
return arrayOp(sc);
}
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
type = e1->type;
if (type->toBasetype()->ty == Tbool)
{
e2 = e2->implicitCastTo(sc, type);
}
typeCombine(sc);
e1->checkIntegral();
e2->checkIntegral();
}
return this;
}
int BinExp::checkSideEffect(int flag)
{
if (op == TOKplusplus ||
op == TOKminusminus ||
op == TOKassign ||
op == TOKconstruct ||
op == TOKblit ||
op == TOKaddass ||
op == TOKminass ||
op == TOKcatass ||
op == TOKmulass ||
op == TOKdivass ||
op == TOKmodass ||
op == TOKshlass ||
op == TOKshrass ||
op == TOKushrass ||
op == TOKandass ||
op == TOKorass ||
op == TOKxorass ||
op == TOKin ||
op == TOKremove)
return 1;
return Expression::checkSideEffect(flag);
}
// generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary
void BinExp::checkComplexMulAssign()
{
// Any multiplication by an imaginary or complex number yields a complex result.
// r *= c, i*=c, r*=i, i*=i are all forbidden operations.
const char *opstr = Token::toChars(op);
if ( e1->type->isreal() && e2->type->iscomplex())
{
error("%s %s %s is undefined. Did you mean %s %s %s.re ?",
e1->type->toChars(), opstr, e2->type->toChars(),
e1->type->toChars(), opstr, e2->type->toChars());
}
else if (e1->type->isimaginary() && e2->type->iscomplex())
{
error("%s %s %s is undefined. Did you mean %s %s %s.im ?",
e1->type->toChars(), opstr, e2->type->toChars(),
e1->type->toChars(), opstr, e2->type->toChars());
}
else if ((e1->type->isreal() || e1->type->isimaginary()) &&
e2->type->isimaginary())
{
error("%s %s %s is an undefined operation", e1->type->toChars(),
opstr, e2->type->toChars());
}
}
// generate an error if this is a nonsensical += or -=, eg real += imaginary
void BinExp::checkComplexAddAssign()
{
// Addition or subtraction of a real and an imaginary is a complex result.
// Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations.
if ( (e1->type->isreal() && (e2->type->isimaginary() || e2->type->iscomplex())) ||
(e1->type->isimaginary() && (e2->type->isreal() || e2->type->iscomplex()))
)
{
error("%s %s %s is undefined (result is complex)",
e1->type->toChars(), Token::toChars(op), e2->type->toChars());
}
}
void BinExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, precedence[op]);
buf->writeByte(' ');
buf->writestring(Token::toChars(op));
buf->writeByte(' ');
expToCBuffer(buf, hgs, e2, (enum PREC)(precedence[op] + 1));
}
int BinExp::isunsigned()
{
return e1->type->isunsigned() || e2->type->isunsigned();
}
#if DMDV2
int BinExp::canThrow()
{
return e1->canThrow() || e2->canThrow();
}
#endif
void BinExp::incompatibleTypes()
{
if (e1->type->toBasetype() != Type::terror &&
e2->type->toBasetype() != Type::terror
)
error("incompatible types for ((%s) %s (%s)): '%s' and '%s'",
e1->toChars(), Token::toChars(op), e2->toChars(),
e1->type->toChars(), e2->type->toChars());
}
/************************************************************/
CompileExp::CompileExp(Loc loc, Expression *e)
: UnaExp(loc, TOKmixin, sizeof(CompileExp), e)
{
}
Expression *CompileExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("CompileExp::semantic('%s')\n", toChars());
#endif
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
if (!e1->type->isString())
{
error("argument to mixin must be a string type, not %s\n", e1->type->toChars());
return new ErrorExp();
}
e1 = e1->optimize(WANTvalue | WANTinterpret);
if (e1->op != TOKstring)
{ error("argument to mixin must be a string, not (%s)", e1->toChars());
return new ErrorExp();
}
StringExp *se = (StringExp *)e1;
se = se->toUTF8(sc);
Parser p(sc->module, (unsigned char *)se->string, se->len, 0);
p.loc = loc;
p.nextToken();
//printf("p.loc.linnum = %d\n", p.loc.linnum);
Expression *e = p.parseExpression();
if (p.token.value != TOKeof)
error("incomplete mixin expression (%s)", se->toChars());
return e->semantic(sc);
}
void CompileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("mixin(");
expToCBuffer(buf, hgs, e1, PREC_assign);
buf->writeByte(')');
}
/************************************************************/
FileExp::FileExp(Loc loc, Expression *e)
: UnaExp(loc, TOKmixin, sizeof(FileExp), e)
{
}
Expression *FileExp::semantic(Scope *sc)
{ char *name;
StringExp *se;
#if LOGSEMANTIC
printf("FileExp::semantic('%s')\n", toChars());
#endif
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e1 = e1->optimize(WANTvalue);
if (e1->op != TOKstring)
{ error("file name argument must be a string, not (%s)", e1->toChars());
goto Lerror;
}
se = (StringExp *)e1;
se = se->toUTF8(sc);
name = (char *)se->string;
if (!global.params.fileImppath)
{ error("need -Jpath switch to import text file %s", name);
goto Lerror;
}
/* 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)
{ error("file %s cannot be found or not in a path specified with -J", se->toChars());
goto Lerror;
}
if (global.params.verbose)
printf("file %s\t(%s)\n", (char *)se->string, name);
{ File f(name);
if (f.read())
{ error("cannot read file %s", f.toChars());
goto Lerror;
}
else
{
f.ref = 1;
se = new StringExp(loc, f.buffer, f.len);
}
}
Lret:
return se->semantic(sc);
Lerror:
se = new StringExp(loc, (char *)"");
goto Lret;
}
void FileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("import(");
expToCBuffer(buf, hgs, e1, PREC_assign);
buf->writeByte(')');
}
/************************************************************/
AssertExp::AssertExp(Loc loc, Expression *e, Expression *msg)
: UnaExp(loc, TOKassert, sizeof(AssertExp), e)
{
this->msg = msg;
}
Expression *AssertExp::syntaxCopy()
{
AssertExp *ae = new AssertExp(loc, e1->syntaxCopy(),
msg ? msg->syntaxCopy() : NULL);
return ae;
}
Expression *AssertExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("AssertExp::semantic('%s')\n", toChars());
#endif
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
// BUG: see if we can do compile time elimination of the Assert
e1 = e1->optimize(WANTvalue);
e1 = e1->checkToBoolean();
if (msg)
{
msg = msg->semantic(sc);
msg = resolveProperties(sc, msg);
msg = msg->implicitCastTo(sc, Type::tchar->arrayOf());
msg = msg->optimize(WANTvalue);
}
if (e1->isBool(FALSE))
{
FuncDeclaration *fd = sc->parent->isFuncDeclaration();
if (fd)
fd->hasReturnExp |= 4;
if (!global.params.useAssert)
{ Expression *e = new HaltExp(loc);
e = e->semantic(sc);
return e;
}
}
type = Type::tvoid;
return this;
}
int AssertExp::checkSideEffect(int flag)
{
return 1;
}
#if DMDV2
int AssertExp::canThrow()
{
/* assert()s are non-recoverable errors, so functions that
* use them can be considered "nothrow"
*/
return 0; //(global.params.useAssert != 0);
}
#endif
void AssertExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("assert(");
expToCBuffer(buf, hgs, e1, PREC_assign);
if (msg)
{
buf->writeByte(',');
expToCBuffer(buf, hgs, msg, PREC_assign);
}
buf->writeByte(')');
}
/************************************************************/
DotIdExp::DotIdExp(Loc loc, Expression *e, Identifier *ident)
: UnaExp(loc, TOKdot, sizeof(DotIdExp), e)
{
this->ident = ident;
}
Expression *DotIdExp::semantic(Scope *sc)
{ Expression *e;
Expression *eleft;
Expression *eright;
#if LOGSEMANTIC
printf("DotIdExp::semantic(this = %p, '%s')\n", this, toChars());
//printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op));
#endif
//{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; }
#if 0
/* Don't do semantic analysis if we'll be converting
* it to a string.
*/
if (ident == Id::stringof)
{ char *s = e1->toChars();
e = new StringExp(loc, s, strlen(s), 'c');
e = e->semantic(sc);
return e;
}
#endif
/* Special case: rewrite this.id and super.id
* to be classtype.id and baseclasstype.id
* if we have no this pointer.
*/
if ((e1->op == TOKthis || e1->op == TOKsuper) && !hasThis(sc))
{ ClassDeclaration *cd;
StructDeclaration *sd;
AggregateDeclaration *ad;
ad = sc->getStructClassScope();
if (ad)
{
cd = ad->isClassDeclaration();
if (cd)
{
if (e1->op == TOKthis)
{
e = typeDotIdExp(loc, cd->type, ident);
return e->semantic(sc);
}
else if (cd->baseClass && e1->op == TOKsuper)
{
e = typeDotIdExp(loc, cd->baseClass->type, ident);
return e->semantic(sc);
}
}
else
{
sd = ad->isStructDeclaration();
if (sd)
{
if (e1->op == TOKthis)
{
e = typeDotIdExp(loc, sd->type, ident);
return e->semantic(sc);
}
}
}
}
}
UnaExp::semantic(sc);
if (e1->op == TOKdotexp)
{
DotExp *de = (DotExp *)e1;
eleft = de->e1;
eright = de->e2;
}
else
{
e1 = resolveProperties(sc, e1);
eleft = NULL;
eright = e1;
}
#if DMDV2
if (e1->op == TOKtuple && ident == Id::offsetof)
{ /* 'distribute' the .offsetof to each of the tuple elements.
*/
TupleExp *te = (TupleExp *)e1;
Expressions *exps = new Expressions();
exps->setDim(te->exps->dim);
for (int i = 0; i < exps->dim; i++)
{ Expression *e = (Expression *)te->exps->data[i];
e = e->semantic(sc);
e = new DotIdExp(e->loc, e, Id::offsetof);
exps->data[i] = (void *)e;
}
e = new TupleExp(loc, exps);
e = e->semantic(sc);
return e;
}
#endif
if (e1->op == TOKtuple && ident == Id::length)
{
TupleExp *te = (TupleExp *)e1;
e = new IntegerExp(loc, te->exps->dim, Type::tsize_t);
return e;
}
if (e1->op == TOKdottd)
{
error("template %s does not have property %s", e1->toChars(), ident->toChars());
return new ErrorExp();
}
if (!e1->type)
{
error("expression %s does not have property %s", e1->toChars(), ident->toChars());
return new ErrorExp();
}
if (eright->op == TOKimport) // also used for template alias's
{
ScopeExp *ie = (ScopeExp *)eright;
/* 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.
*/
Dsymbol *s = ie->sds->search(loc, ident,
(ie->sds->isModule() && ie->sds != sc->module) ? 1 : 0);
if (s)
{
s = s->toAlias();
checkDeprecated(sc, s);
EnumMember *em = s->isEnumMember();
if (em)
{
e = em->value;
e = e->semantic(sc);
return e;
}
VarDeclaration *v = s->isVarDeclaration();
if (v)
{
//printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v->type->toChars());
if (v->inuse)
{
error("circular reference to '%s'", v->toChars());
type = Type::tint32;
return this;
}
type = v->type;
if (v->isSameAsInitializer())
{
if (v->init)
{
ExpInitializer *ei = v->init->isExpInitializer();
if (ei)
{
//printf("\tei: %p (%s)\n", ei->exp, ei->exp->toChars());
//ei->exp = ei->exp->semantic(sc);
if (ei->exp->type == type)
{
e = ei->exp->copy(); // make copy so we can change loc
e->loc = loc;
return e;
}
}
}
else if (type->isscalar())
{
e = type->defaultInit();
e->loc = loc;
return e;
}
}
if (v->needThis())
{
if (!eleft)
eleft = new ThisExp(loc);
e = new DotVarExp(loc, eleft, v);
e = e->semantic(sc);
}
else
{
e = new VarExp(loc, v);
if (eleft)
{ e = new CommaExp(loc, eleft, e);
e->type = v->type;
}
}
e = e->deref();
return e->semantic(sc);
}
FuncDeclaration *f = s->isFuncDeclaration();
if (f)
{
//printf("it's a function\n");
if (f->needThis())
{
if (!eleft)
eleft = new ThisExp(loc);
e = new DotVarExp(loc, eleft, f);
e = e->semantic(sc);
}
else
{
e = new VarExp(loc, f);
if (eleft)
{ e = new CommaExp(loc, eleft, e);
e->type = f->type;
}
}
return e;
}
#if DMDV2
OverloadSet *o = s->isOverloadSet();
if (o)
{ //printf("'%s' is an overload set\n", o->toChars());
return new OverExp(o);
}
#endif
Type *t = s->getType();
if (t)
{
return new TypeExp(loc, t);
}
TupleDeclaration *tup = s->isTupleDeclaration();
if (tup)
{
if (eleft)
error("cannot have e.tuple");
e = new TupleExp(loc, tup);
e = e->semantic(sc);
return e;
}
ScopeDsymbol *sds = s->isScopeDsymbol();
if (sds)
{
//printf("it's a ScopeDsymbol\n");
e = new ScopeExp(loc, sds);
e = e->semantic(sc);
if (eleft)
e = new DotExp(loc, eleft, e);
return e;
}
Import *imp = s->isImport();
if (imp)
{
ScopeExp *ie;
ie = new ScopeExp(loc, imp->pkg);
return ie->semantic(sc);
}
// BUG: handle other cases like in IdentifierExp::semantic()
#ifdef DEBUG
printf("s = '%s', kind = '%s'\n", s->toChars(), s->kind());
#endif
assert(0);
}
else if (ident == Id::stringof)
{ char *s = ie->toChars();
e = new StringExp(loc, s, strlen(s), 'c');
e = e->semantic(sc);
return e;
}
error("undefined identifier %s", toChars());
return new ErrorExp();
}
else if (e1->type->ty == Tpointer &&
ident != Id::init && ident != Id::__sizeof &&
ident != Id::__xalignof && ident != Id::offsetof &&
ident != Id::mangleof && ident != Id::stringof)
{ /* Rewrite:
* p.ident
* as:
* (*p).ident
*/
e = new PtrExp(loc, e1);
e->type = ((TypePointer *)e1->type)->next;
return e->type->dotExp(sc, e, ident);
}
#if DMDV2
else if (t1b->ty == Tarray ||
t1b->ty == Tsarray ||
t1b->ty == Taarray)
{ /* If ident is not a valid property, rewrite:
* e1.ident
* as:
* .ident(e1)
*/
unsigned errors = global.errors;
global.gag++;
Type *t1 = e1->type;
e = e1->type->dotExp(sc, e1, ident);
global.gag--;
if (errors != global.errors) // if failed to find the property
{
global.errors = errors;
e1->type = t1; // kludge to restore type
e = new DotIdExp(loc, new IdentifierExp(loc, Id::empty), ident);
e = new CallExp(loc, e, e1);
}
e = e->semantic(sc);
return e;
}
#endif
else
{
e = e1->type->dotExp(sc, e1, ident);
e = e->semantic(sc);
return e;
}
}
void DotIdExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
//printf("DotIdExp::toCBuffer()\n");
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
buf->writestring(ident->toChars());
}
/********************** DotTemplateExp ***********************************/
// Mainly just a placeholder
DotTemplateExp::DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td)
: UnaExp(loc, TOKdottd, sizeof(DotTemplateExp), e)
{
this->td = td;
}
void DotTemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
buf->writestring(td->toChars());
}
/************************************************************/
DotVarExp::DotVarExp(Loc loc, Expression *e, Declaration *v)
: UnaExp(loc, TOKdotvar, sizeof(DotVarExp), e)
{
//printf("DotVarExp()\n");
this->var = v;
}
Expression *DotVarExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("DotVarExp::semantic('%s')\n", toChars());
#endif
if (!type)
{
var = var->toAlias()->isDeclaration();
TupleDeclaration *tup = var->isTupleDeclaration();
if (tup)
{ /* Replace:
* e1.tuple(a, b, c)
* with:
* tuple(e1.a, e1.b, e1.c)
*/
Expressions *exps = new Expressions;
exps->reserve(tup->objects->dim);
for (size_t i = 0; i < tup->objects->dim; i++)
{ Object *o = (Object *)tup->objects->data[i];
if (o->dyncast() != DYNCAST_EXPRESSION)
{
error("%s is not an expression", o->toChars());
}
else
{
Expression *e = (Expression *)o;
if (e->op != TOKdsymbol)
error("%s is not a member", e->toChars());
else
{ DsymbolExp *ve = (DsymbolExp *)e;
e = new DotVarExp(loc, e1, ve->s->isDeclaration());
exps->push(e);
}
}
}
Expression *e = new TupleExp(loc, exps);
e = e->semantic(sc);
return e;
}
e1 = e1->semantic(sc);
type = var->type;
if (!type && global.errors)
{ // var is goofed up, just return 0
return new ErrorExp();
}
assert(type);
if (!var->isFuncDeclaration()) // for functions, do checks after overload resolution
{
Dsymbol *vparent = var->toParent();
AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL;
e1 = getRightThis(loc, sc, ad, e1, var);
if (!sc->noaccesscheck)
accessCheck(loc, sc, e1, var);
VarDeclaration *v = var->isVarDeclaration();
if (v && v->isSameAsInitializer())
{ ExpInitializer *ei = v->getExpInitializer();
if (ei)
{ Expression *e = ei->exp->copy();
e = e->semantic(sc);
return e;
}
if (v->init)
{ Expression *e = v->init->toExpression();
if (e)
{ e = e->copy();
e = e->semantic(sc);
return e;
}
}
}
}
}
//printf("-DotVarExp::semantic('%s')\n", toChars());
return this;
}
#if DMDV2
int DotVarExp::isLvalue()
{
return 1;
}
#endif
Expression *DotVarExp::toLvalue(Scope *sc, Expression *e)
{
//printf("DotVarExp::toLvalue(%s)\n", toChars());
return this;
}
Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e)
{
#if 0
printf("DotVarExp::modifiableLvalue(%s)\n", toChars());
printf("e1->type = %s\n", e1->type->toChars());
printf("var->type = %s\n", var->type->toChars());
#endif
if (var->isCtorinit())
{ // It's only modifiable if inside the right constructor
Dsymbol *s = sc->func;
while (1)
{
FuncDeclaration *fd = NULL;
if (s)
fd = s->isFuncDeclaration();
if (fd &&
((fd->isCtorDeclaration() && var->storage_class & STCfield) ||
(fd->isStaticCtorDeclaration() && !(var->storage_class & STCfield))) &&
fd->toParent() == var->toParent() &&
e1->op == TOKthis
)
{
VarDeclaration *v = var->isVarDeclaration();
assert(v);
v->ctorinit = 1;
//printf("setting ctorinit\n");
}
else
{
if (s)
{ s = s->toParent2();
continue;
}
else
{
const char *p = var->isStatic() ? "static " : "";
error("can only initialize %sconst member %s inside %sconstructor",
p, var->toChars(), p);
}
}
break;
}
}
#if DMDV2
else
{
Type *t1 = e1->type->toBasetype();
if (!t1->isMutable() ||
(t1->ty == Tpointer && !t1->nextOf()->isMutable()) ||
!var->type->isMutable() ||
!var->type->isAssignable() ||
var->storage_class & STCmanifest
)
error("cannot modify const/immutable expression %s", toChars());
}
#endif
return this;
}
void DotVarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
buf->writestring(var->toChars());
}
/************************************************************/
/* Things like:
* foo.bar!(args)
*/
DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, TemplateInstance *ti)
: UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e)
{
//printf("DotTemplateInstanceExp()\n");
this->ti = ti;
}
Expression *DotTemplateInstanceExp::syntaxCopy()
{
DotTemplateInstanceExp *de = new DotTemplateInstanceExp(loc,
e1->syntaxCopy(),
(TemplateInstance *)ti->syntaxCopy(NULL));
return de;
}
Expression *DotTemplateInstanceExp::semantic(Scope *sc)
{
#if 1
#if LOGSEMANTIC
printf("DotTemplateInstanceExp::semantic('%s')\n", toChars());
#endif
Expression *eleft;
Expression *e = new DotIdExp(loc, e1, ti->name);
L1:
e = e->semantic(sc);
if (e->op == TOKdottd)
{
if (global.errors)
return new ErrorExp(); // TemplateInstance::semantic() will fail anyway
DotTemplateExp *dte = (DotTemplateExp *)e;
TemplateDeclaration *td = dte->td;
eleft = dte->e1;
ti->tempdecl = td;
ti->semantic(sc);
if (!ti->inst) // if template failed to expand
return new ErrorExp();
Dsymbol *s = ti->inst->toAlias();
Declaration *v = s->isDeclaration();
if (v)
{
/* Fix for Bugzilla 4003
* The problem is a class template member function v returning a reference to the same
* type as the enclosing template instantiation. This results in a nested instantiation,
* which of course gets short circuited. The return type then gets set to
* the template instance type before instantiation, rather than after.
* We can detect this by the deco not being set. If so, go ahead and retry
* the return type semantic.
* The offending code is the return type from std.typecons.Tuple.slice:
* ref Tuple!(Types[from .. to]) slice(uint from, uint to)()
* {
* return *cast(typeof(return) *) &(field[from]);
* }
* and this line from the following unittest:
* auto s = a.slice!(1, 3);
* where s's type wound up not having semantic() run on it.
*/
if (v->type && !v->type->deco)
v->type = v->type->semantic(v->loc, sc);
e = new DotVarExp(loc, eleft, v);
e = e->semantic(sc);
return e;
}
e = new ScopeExp(loc, ti);
e = new DotExp(loc, eleft, e);
e = e->semantic(sc);
return e;
}
else if (e->op == TOKimport)
{ ScopeExp *se = (ScopeExp *)e;
TemplateDeclaration *td = se->sds->isTemplateDeclaration();
if (!td)
{ error("%s is not a template", e->toChars());
return new ErrorExp();
}
ti->tempdecl = td;
e = new ScopeExp(loc, ti);
e = e->semantic(sc);
return e;
}
else if (e->op == TOKdotexp)
{ DotExp *de = (DotExp *)e;
if (de->e2->op == TOKimport)
{ // This should *really* be moved to ScopeExp::semantic()
ScopeExp *se = (ScopeExp *)de->e2;
de->e2 = new DsymbolExp(loc, se->sds);
de->e2 = de->e2->semantic(sc);
}
if (de->e2->op == TOKtemplate)
{ TemplateExp *te = (TemplateExp *) de->e2;
e = new DotTemplateExp(loc,de->e1,te->td);
}
goto L1;
}
error("%s isn't a template", e->toChars());
return new ErrorExp();
#else
Dsymbol *s;
Dsymbol *s2;
TemplateDeclaration *td;
Expression *e;
Identifier *id;
Type *t1;
Expression *eleft = NULL;
Expression *eright;
#if LOGSEMANTIC
printf("DotTemplateInstanceExp::semantic('%s')\n", toChars());
#endif
//e1->print();
//print();
e1 = e1->semantic(sc);
t1 = e1->type;
if (t1)
t1 = t1->toBasetype();
//t1->print();
/* Extract the following from e1:
* s: the symbol which ti should be a member of
* eleft: if not NULL, it is the 'this' pointer for ti
*/
if (e1->op == TOKdotexp)
{ DotExp *de = (DotExp *)e1;
eleft = de->e1;
eright = de->e2;
}
else
{ eleft = NULL;
eright = e1;
}
if (eright->op == TOKimport)
{
s = ((ScopeExp *)eright)->sds;
}
else if (e1->op == TOKtype)
{
s = t1->isClassHandle();
if (!s)
{ if (t1->ty == Tstruct)
s = ((TypeStruct *)t1)->sym;
else
goto L1;
}
}
else if (t1 && (t1->ty == Tstruct || t1->ty == Tclass))
{
s = t1->toDsymbol(sc);
eleft = e1;
}
else if (t1 && t1->ty == Tpointer)
{
t1 = ((TypePointer *)t1)->next->toBasetype();
if (t1->ty != Tstruct)
goto L1;
s = t1->toDsymbol(sc);
eleft = e1;
}
else
{
L1:
error("template %s is not a member of %s", ti->toChars(), e1->toChars());
goto Lerr;
}
assert(s);
id = ti->name;
s2 = s->search(loc, id, 0);
if (!s2)
{
if (!s->ident)
error("template identifier %s is not a member of undefined %s", id->toChars(), s->kind());
else
error("template identifier %s is not a member of %s %s", id->toChars(), s->kind(), s->ident->toChars());
goto Lerr;
}
s = s2;
s->semantic(sc);
s = s->toAlias();
td = s->isTemplateDeclaration();
if (!td)
{
error("%s is not a template", id->toChars());
goto Lerr;
}
if (global.errors)
goto Lerr;
ti->tempdecl = td;
if (eleft)
{ Declaration *v;
ti->semantic(sc);
s = ti->inst->toAlias();
v = s->isDeclaration();
if (v)
{ e = new DotVarExp(loc, eleft, v);
e = e->semantic(sc);
return e;
}
}
e = new ScopeExp(loc, ti);
if (eleft)
{
e = new DotExp(loc, eleft, e);
}
e = e->semantic(sc);
return e;
Lerr:
return new IntegerExp(loc, 0, Type::tint32);
#endif
}
void DotTemplateInstanceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
ti->toCBuffer(buf, hgs);
}
/************************************************************/
DelegateExp::DelegateExp(Loc loc, Expression *e, FuncDeclaration *f)
: UnaExp(loc, TOKdelegate, sizeof(DelegateExp), e)
{
this->func = f;
m = NULL;
}
Expression *DelegateExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("DelegateExp::semantic('%s')\n", toChars());
#endif
if (!type)
{
m = sc->module;
e1 = e1->semantic(sc);
// LDC we need a copy as we store the LLVM tpye in TypeFunction, and delegate/members have different types for 'this'
type = new TypeDelegate(func->type->syntaxCopy());
type = type->semantic(loc, sc);
AggregateDeclaration *ad = func->toParent()->isAggregateDeclaration();
if (func->needThis())
e1 = getRightThis(loc, sc, ad, e1, func);
if (ad && ad->isClassDeclaration() && ad->type != e1->type)
{ // A downcast is required for interfaces, see Bugzilla 3706
e1 = new CastExp(loc, e1, ad->type);
e1 = e1->semantic(sc);
}
}
return this;
}
void DelegateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writeByte('&');
if (!func->isNested())
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
}
buf->writestring(func->toChars());
}
/************************************************************/
DotTypeExp::DotTypeExp(Loc loc, Expression *e, Dsymbol *s)
: UnaExp(loc, TOKdottype, sizeof(DotTypeExp), e)
{
this->sym = s;
this->type = s->getType();
}
Expression *DotTypeExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("DotTypeExp::semantic('%s')\n", toChars());
#endif
UnaExp::semantic(sc);
return this;
}
void DotTypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
buf->writestring(sym->toChars());
}
/************************************************************/
CallExp::CallExp(Loc loc, Expression *e, Expressions *exps)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
this->arguments = exps;
}
CallExp::CallExp(Loc loc, Expression *e)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
this->arguments = NULL;
}
CallExp::CallExp(Loc loc, Expression *e, Expression *earg1)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
Expressions *arguments = new Expressions();
if (earg1)
{ arguments->setDim(1);
arguments->data[0] = (void *)earg1;
}
this->arguments = arguments;
}
CallExp::CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2)
: UnaExp(loc, TOKcall, sizeof(CallExp), e)
{
Expressions *arguments = new Expressions();
arguments->setDim(2);
arguments->data[0] = (void *)earg1;
arguments->data[1] = (void *)earg2;
this->arguments = arguments;
}
Expression *CallExp::syntaxCopy()
{
return new CallExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
}
Expression *CallExp::semantic(Scope *sc)
{
TypeFunction *tf;
FuncDeclaration *f;
int i;
Type *t1;
int istemp;
Objects *targsi = NULL; // initial list of template arguments
#if LOGSEMANTIC
printf("CallExp::semantic() %s\n", toChars());
#endif
if (type)
return this; // semantic() already run
#if 0
if (arguments && arguments->dim)
{
Expression *earg = (Expression *)arguments->data[0];
earg->print();
if (earg->type) earg->type->print();
}
#endif
if (e1->op == TOKdelegate)
{ DelegateExp *de = (DelegateExp *)e1;
e1 = new DotVarExp(de->loc, de->e1, de->func);
return semantic(sc);
}
/* Transform:
* array.id(args) into id(array,args)
* aa.remove(arg) into delete aa[arg]
*/
if (e1->op == TOKdot)
{
// BUG: we should handle array.a.b.c.e(args) too
DotIdExp *dotid = (DotIdExp *)(e1);
dotid->e1 = dotid->e1->semantic(sc);
assert(dotid->e1);
if (dotid->e1->type)
{
TY e1ty = dotid->e1->type->toBasetype()->ty;
if (e1ty == Taarray && dotid->ident == Id::remove)
{
if (!arguments || arguments->dim != 1)
{ error("expected key as argument to aa.remove()");
goto Lagain;
}
Expression *key = (Expression *)arguments->data[0];
key = key->semantic(sc);
key = resolveProperties(sc, key);
key->rvalue();
TypeAArray *taa = (TypeAArray *)dotid->e1->type->toBasetype();
key = key->implicitCastTo(sc, taa->index);
key = key->implicitCastTo(sc, taa->key);
return new RemoveExp(loc, dotid->e1, key);
}
else if (e1ty == Tarray || e1ty == Tsarray || e1ty == Taarray)
{
if (!arguments)
arguments = new Expressions();
arguments->shift(dotid->e1);
#if DMDV2
e1 = new DotIdExp(dotid->loc, new IdentifierExp(dotid->loc, Id::empty), dotid->ident);
#else
e1 = new IdentifierExp(dotid->loc, dotid->ident);
#endif
}
}
}
#if 1
/* This recognizes:
* foo!(tiargs)(funcargs)
*/
if (e1->op == TOKimport && !e1->type)
{ ScopeExp *se = (ScopeExp *)e1;
TemplateInstance *ti = se->sds->isTemplateInstance();
if (ti && !ti->semanticRun)
{
/* Attempt to instantiate ti. If that works, go with it.
* If not, go with partial explicit specialization.
*/
ti->semanticTiargs(sc);
unsigned errors = global.errors;
global.gag++;
ti->semantic(sc);
global.gag--;
if (errors != global.errors)
{
/* Didn't work, go with partial explicit specialization
*/
global.errors = errors;
targsi = ti->tiargs;
e1 = new IdentifierExp(loc, ti->name);
}
}
}
/* This recognizes:
* expr.foo!(tiargs)(funcargs)
*/
if (e1->op == TOKdotti && !e1->type)
{ DotTemplateInstanceExp *se = (DotTemplateInstanceExp *)e1;
TemplateInstance *ti = se->ti;
if (!ti->semanticRun)
{
/* Attempt to instantiate ti. If that works, go with it.
* If not, go with partial explicit specialization.
*/
ti->semanticTiargs(sc);
Expression *etmp;
unsigned errors = global.errors;
global.gag++;
etmp = e1->semantic(sc);
global.gag--;
if (errors != global.errors)
{
global.errors = errors;
targsi = ti->tiargs;
e1 = new DotIdExp(loc, se->e1, ti->name);
}
else
e1 = etmp;
}
}
#endif
istemp = 0;
Lagain:
//printf("Lagain: %s\n", toChars());
f = NULL;
if (e1->op == TOKthis || e1->op == TOKsuper)
{
// semantic() run later for these
}
else
{
UnaExp::semantic(sc);
/* Look for e1 being a lazy parameter
*/
if (e1->op == TOKvar)
{ VarExp *ve = (VarExp *)e1;
if (ve->var->storage_class & STClazy)
{
TypeFunction *tf = new TypeFunction(NULL, ve->var->type, 0, LINKd);
TypeDelegate *t = new TypeDelegate(tf);
ve->type = t->semantic(loc, sc);
}
}
if (e1->op == TOKimport)
{ // Perhaps this should be moved to ScopeExp::semantic()
ScopeExp *se = (ScopeExp *)e1;
e1 = new DsymbolExp(loc, se->sds);
e1 = e1->semantic(sc);
}
#if 1 // patch for #540 by Oskar Linde
else if (e1->op == TOKdotexp)
{
DotExp *de = (DotExp *) e1;
if (de->e2->op == TOKimport)
{ // This should *really* be moved to ScopeExp::semantic()
ScopeExp *se = (ScopeExp *)de->e2;
de->e2 = new DsymbolExp(loc, se->sds);
de->e2 = de->e2->semantic(sc);
}
if (de->e2->op == TOKtemplate)
{ TemplateExp *te = (TemplateExp *) de->e2;
e1 = new DotTemplateExp(loc,de->e1,te->td);
}
}
#endif
}
if (e1->op == TOKcomma)
{
CommaExp *ce = (CommaExp *)e1;
e1 = ce->e2;
e1->type = ce->type;
ce->e2 = this;
ce->type = NULL;
return ce->semantic(sc);
}
t1 = NULL;
if (e1->type)
t1 = e1->type->toBasetype();
// Check for call operator overload
if (t1)
{ AggregateDeclaration *ad;
if (t1->ty == Tstruct)
{
ad = ((TypeStruct *)t1)->sym;
#if DMDV2
// First look for constructor
if (ad->ctor && arguments && arguments->dim)
{
// Create variable that will get constructed
Identifier *idtmp = Lexer::uniqueId("__ctmp");
VarDeclaration *tmp = new VarDeclaration(loc, t1, idtmp, NULL);
Expression *av = new DeclarationExp(loc, tmp);
av = new CommaExp(loc, av, new VarExp(loc, tmp));
Expression *e;
CtorDeclaration *cf = ad->ctor->isCtorDeclaration();
if (cf)
e = new DotVarExp(loc, av, cf, 1);
else
{ TemplateDeclaration *td = ad->ctor->isTemplateDeclaration();
assert(td);
e = new DotTemplateExp(loc, av, td);
}
e = new CallExp(loc, e, arguments);
#if !STRUCTTHISREF
/* Constructors return a pointer to the instance
*/
e = new PtrExp(loc, e);
#endif
e = e->semantic(sc);
return e;
}
#endif
// No constructor, look for overload of opCall
if (search_function(ad, Id::call))
goto L1; // overload of opCall, therefore it's a call
if (e1->op != TOKtype)
error("%s %s does not overload ()", ad->kind(), ad->toChars());
/* It's a struct literal
*/
Expression *e = new StructLiteralExp(loc, (StructDeclaration *)ad, arguments);
e = e->semantic(sc);
e->type = e1->type; // in case e1->type was a typedef
return e;
}
else if (t1->ty == Tclass)
{
ad = ((TypeClass *)t1)->sym;
goto L1;
L1:
// Rewrite as e1.call(arguments)
Expression *e = new DotIdExp(loc, e1, Id::call);
e = new CallExp(loc, e, arguments);
e = e->semantic(sc);
return e;
}
}
arrayExpressionSemantic(arguments, sc);
preFunctionParameters(loc, sc, arguments);
if (e1->op == TOKdotvar && t1->ty == Tfunction ||
e1->op == TOKdottd)
{
DotVarExp *dve;
DotTemplateExp *dte;
AggregateDeclaration *ad;
UnaExp *ue = (UnaExp *)(e1);
if (e1->op == TOKdotvar)
{ // Do overload resolution
dve = (DotVarExp *)(e1);
f = dve->var->isFuncDeclaration();
assert(f);
f = f->overloadResolve(loc, NULL, arguments, sc->module);
ad = f->toParent()->isAggregateDeclaration();
}
else
{ dte = (DotTemplateExp *)(e1);
TemplateDeclaration *td = dte->td;
assert(td);
if (!arguments)
// Should fix deduceFunctionTemplate() so it works on NULL argument
arguments = new Expressions();
f = td->deduceFunctionTemplate(sc, loc, targsi, NULL, arguments);
if (!f)
{ type = Type::terror;
return this;
}
ad = td->toParent()->isAggregateDeclaration();
}
if (f->needThis())
{
ue->e1 = getRightThis(loc, sc, ad, ue->e1, f);
}
/* 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 &&
f->addPostInvariant()
)
{
error("cannot call public/export function %s from invariant", f->toChars());
}
checkDeprecated(sc, f);
#if DMDV2
checkPurity(sc, f);
#endif
accessCheck(loc, sc, ue->e1, f);
if (!f->needThis())
{
VarExp *ve = new VarExp(loc, f);
e1 = new CommaExp(loc, ue->e1, ve);
e1->type = f->type;
}
else
{
if (e1->op == TOKdotvar)
dve->var = f;
else
e1 = new DotVarExp(loc, dte->e1, f);
e1->type = f->type;
// See if we need to adjust the 'this' pointer
AggregateDeclaration *ad = f->isThis();
ClassDeclaration *cd = ue->e1->type->isClassHandle();
if (ad && cd && ad->isClassDeclaration() && ad != cd &&
ue->e1->op != TOKsuper)
{
ue->e1 = ue->e1->castTo(sc, ad->type); //new CastExp(loc, ue->e1, ad->type);
ue->e1 = ue->e1->semantic(sc);
}
}
t1 = e1->type;
}
else if (e1->op == TOKsuper)
{
// Base class constructor call
ClassDeclaration *cd = NULL;
if (sc->func)
cd = sc->func->toParent()->isClassDeclaration();
if (!cd || !cd->baseClass || !sc->func->isCtorDeclaration())
{
error("super class constructor call must be in a constructor");
type = Type::terror;
return this;
}
else
{
f = cd->baseClass->ctor;
if (!f)
{ error("no super class constructor for %s", cd->baseClass->toChars());
type = Type::terror;
return this;
}
else
{
if (!sc->intypeof)
{
#if 0
if (sc->callSuper & (CSXthis | CSXsuper))
error("reference to this before super()");
#endif
if (sc->noctor || sc->callSuper & CSXlabel)
error("constructor calls not allowed in loops or after labels");
if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
error("multiple constructor calls");
sc->callSuper |= CSXany_ctor | CSXsuper_ctor;
}
f = f->overloadResolve(loc, NULL, arguments, sc->module);
checkDeprecated(sc, f);
#if DMDV2
checkPurity(sc, f);
#endif
e1 = new DotVarExp(e1->loc, e1, f);
e1 = e1->semantic(sc);
t1 = e1->type;
}
}
}
else if (e1->op == TOKthis)
{
// same class constructor call
ClassDeclaration *cd = NULL;
if (sc->func)
cd = sc->func->toParent()->isClassDeclaration();
if (!cd || !sc->func->isCtorDeclaration())
{
error("class constructor call must be in a constructor");
type = Type::terror;
return this;
}
else
{
if (!sc->intypeof)
{
#if 0
if (sc->callSuper & (CSXthis | CSXsuper))
error("reference to this before super()");
#endif
if (sc->noctor || sc->callSuper & CSXlabel)
error("constructor calls not allowed in loops or after labels");
if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor))
error("multiple constructor calls");
sc->callSuper |= CSXany_ctor | CSXthis_ctor;
}
f = cd->ctor;
f = f->overloadResolve(loc, NULL, arguments, sc->module);
checkDeprecated(sc, f);
#if DMDV2
checkPurity(sc, f);
#endif
e1 = new DotVarExp(e1->loc, e1, f);
e1 = e1->semantic(sc);
t1 = e1->type;
// BUG: this should really be done by checking the static
// call graph
if (f == sc->func)
error("cyclic constructor call");
}
}
else if (!t1)
{
error("function expected before (), not '%s'", e1->toChars());
type = Type::terror;
return this;
}
else if (t1->ty != Tfunction)
{
if (t1->ty == Tdelegate)
{ TypeDelegate *td = (TypeDelegate *)t1;
assert(td->next->ty == Tfunction);
tf = (TypeFunction *)(td->next);
goto Lcheckargs;
}
else if (t1->ty == Tpointer && ((TypePointer *)t1)->next->ty == Tfunction)
{ Expression *e;
e = new PtrExp(loc, e1);
t1 = ((TypePointer *)t1)->next;
e->type = t1;
e1 = e;
}
else if (e1->op == TOKtemplate)
{
TemplateExp *te = (TemplateExp *)e1;
f = te->td->deduceFunctionTemplate(sc, loc, targsi, NULL, arguments);
if (!f)
{ type = Type::terror;
return this;
}
if (f->needThis() && hasThis(sc))
{
// Supply an implicit 'this', as in
// this.ident
e1 = new DotTemplateExp(loc, (new ThisExp(loc))->semantic(sc), te->td);
goto Lagain;
}
e1 = new VarExp(loc, f);
goto Lagain;
}
else
{ error("function expected before (), not %s of type %s", e1->toChars(), e1->type->toChars());
type = Type::terror;
return this;
}
}
else if (e1->op == TOKvar)
{
// Do overload resolution
VarExp *ve = (VarExp *)e1;
f = ve->var->isFuncDeclaration();
assert(f);
// Look to see if f is really a function template
if (0 && !istemp && f->parent)
{ TemplateInstance *ti = f->parent->isTemplateInstance();
if (ti &&
(ti->name == f->ident ||
ti->toAlias()->ident == f->ident)
&&
ti->tempdecl)
{
/* This is so that one can refer to the enclosing
* template, even if it has the same name as a member
* of the template, if it has a !(arguments)
*/
TemplateDeclaration *tempdecl = ti->tempdecl;
if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's
tempdecl = tempdecl->overroot; // then get the start
e1 = new TemplateExp(loc, tempdecl);
istemp = 1;
goto Lagain;
}
}
f = f->overloadResolve(loc, NULL, arguments, sc->module);
checkDeprecated(sc, f);
#if DMDV2
checkPurity(sc, f);
#endif
if (f->needThis() && hasThis(sc))
{
// Supply an implicit 'this', as in
// this.ident
e1 = new DotVarExp(loc, new ThisExp(loc), f);
goto Lagain;
}
accessCheck(loc, sc, NULL, f);
ve->var = f;
ve->type = f->type;
t1 = f->type;
}
assert(t1->ty == Tfunction);
tf = (TypeFunction *)(t1);
Lcheckargs:
assert(tf->ty == Tfunction);
type = tf->next;
if (!arguments)
arguments = new Expressions();
functionParameters(loc, sc, tf, arguments);
assert(type);
if (f && f->tintro)
{
Type *t = type;
int offset = 0;
TypeFunction *tf = (TypeFunction *)f->tintro;
if (tf->next->isBaseOf(t, &offset) && offset)
{
type = tf->next;
return castTo(sc, t);
}
}
return this;
}
int CallExp::checkSideEffect(int flag)
{
#if DMDV2
if (flag != 2)
return 1;
if (e1->checkSideEffect(2))
return 1;
/* If any of the arguments have side effects, this expression does
*/
for (size_t i = 0; i < arguments->dim; i++)
{ Expression *e = (Expression *)arguments->data[i];
if (e->checkSideEffect(2))
return 1;
}
/* If calling a function or delegate that is typed as pure,
* then this expression has no side effects.
*/
Type *t = e1->type->toBasetype();
if (t->ty == Tfunction && ((TypeFunction *)t)->ispure)
return 0;
if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->ispure)
return 0;
#endif
return 1;
}
#if DMDV2
int CallExp::canThrow()
{
//printf("CallExp::canThrow() %s\n", toChars());
if (e1->canThrow())
return 1;
/* If any of the arguments can throw, then this expression can throw
*/
for (size_t i = 0; i < arguments->dim; i++)
{ Expression *e = (Expression *)arguments->data[i];
if (e && e->canThrow())
return 1;
}
if (global.errors && !e1->type)
return 0; // error recovery
/* If calling a function or delegate that is typed as nothrow,
* then this expression cannot throw.
* Note that pure functions can throw.
*/
Type *t = e1->type->toBasetype();
if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow)
return 0;
if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow)
return 0;
return 1;
}
#endif
#if DMDV2
int CallExp::isLvalue()
{
// if (type->toBasetype()->ty == Tstruct)
// return 1;
Type *tb = e1->type->toBasetype();
if (tb->ty == Tfunction && ((TypeFunction *)tb)->isref)
return 1; // function returns a reference
return 0;
}
#endif
Expression *CallExp::toLvalue(Scope *sc, Expression *e)
{
#if 1
if (type->toBasetype()->ty == Tstruct)
return this;
else
#endif
return Expression::toLvalue(sc, e);
}
Expression *CallExp::modifiableLvalue(Scope *sc, Expression *e)
{
#if 1
return Expression::modifiableLvalue(sc, e);
#else
/* Although function return values being usable as "ref" parameters is
* unsound, disabling it breaks existing code.
* Bugzilla 3167
*/
error("cannot assign to function call");
return toLvalue(sc, e);
#endif
}
void CallExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
if (e1->op == TOKtype)
/* Avoid parens around type to prevent forbidden cast syntax:
* (sometype)(arg1)
* This is ok since types in constructor calls
* can never depend on parens anyway
*/
e1->toCBuffer(buf, hgs);
else
expToCBuffer(buf, hgs, e1, precedence[op]);
buf->writeByte('(');
argsToCBuffer(buf, arguments, hgs);
buf->writeByte(')');
}
/************************************************************/
AddrExp::AddrExp(Loc loc, Expression *e)
: UnaExp(loc, TOKaddress, sizeof(AddrExp), e)
{
m = NULL;
}
Expression *AddrExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("AddrExp::semantic('%s')\n", toChars());
#endif
if (!type)
{
m = sc->module;
UnaExp::semantic(sc);
e1 = e1->toLvalue(sc, NULL);
if (!e1->type)
{
error("cannot take address of %s", e1->toChars());
return new ErrorExp();
}
if (!e1->type->deco)
{
/* No deco means semantic() was not run on the type.
* We have to run semantic() on the symbol to get the right type:
* auto x = &bar;
* pure: int bar() { return 1;}
* otherwise the 'pure' is missing from the type assigned to x.
*/
error("forward reference to %s", e1->toChars());
return new ErrorExp();
}
type = e1->type->pointerTo();
// See if this should really be a delegate
if (e1->op == TOKdotvar)
{
DotVarExp *dve = (DotVarExp *)e1;
FuncDeclaration *f = dve->var->isFuncDeclaration();
if (f)
{ Expression *e = new DelegateExp(loc, dve->e1, f);
e = e->semantic(sc);
return e;
}
}
else if (e1->op == TOKvar)
{
VarExp *dve = (VarExp *)e1;
FuncDeclaration *f = dve->var->isFuncDeclaration();
VarDeclaration *v = dve->var->isVarDeclaration();
// LDC
if (f && f->isIntrinsic())
{
error("cannot take the address of intrinsic function %s", e1->toChars());
return this;
}
if (f && f->isNested())
{ Expression *e;
e = new DelegateExp(loc, e1, f);
e = e->semantic(sc);
return e;
}
}
else if (e1->op == TOKarray)
{
if (e1->type->toBasetype()->ty == Tbit)
error("cannot take address of bit in array");
}
return optimize(WANTvalue);
}
return this;
}
void AddrExp::checkEscape()
{
e1->checkEscapeRef();
}
/************************************************************/
PtrExp::PtrExp(Loc loc, Expression *e)
: UnaExp(loc, TOKstar, sizeof(PtrExp), e)
{
// if (e->type)
// type = ((TypePointer *)e->type)->next;
}
PtrExp::PtrExp(Loc loc, Expression *e, Type *t)
: UnaExp(loc, TOKstar, sizeof(PtrExp), e)
{
type = t;
}
Expression *PtrExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("PtrExp::semantic('%s')\n", toChars());
#endif
if (!type)
{
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
if (!e1->type)
printf("PtrExp::semantic('%s')\n", toChars());
Type *tb = e1->type->toBasetype();
switch (tb->ty)
{
case Tpointer:
type = tb->next;
break;
case Tsarray:
case Tarray:
type = tb->next;
e1 = e1->castTo(sc, type->pointerTo());
break;
default:
error("can only * a pointer, not a '%s'", e1->type->toChars());
case Terror:
return new ErrorExp();
}
rvalue();
}
return this;
}
#if DMDV2
int PtrExp::isLvalue()
{
return 1;
}
#endif
void PtrExp::checkEscapeRef()
{
e1->checkEscape();
}
Expression *PtrExp::toLvalue(Scope *sc, Expression *e)
{
#if 0
tym = tybasic(e1->ET->Tty);
if (!(tyscalar(tym) ||
tym == TYstruct ||
tym == TYarray && e->Eoper == TOKaddr))
synerr(EM_lvalue); // lvalue expected
#endif
return this;
}
#if DMDV2
Expression *PtrExp::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type->toChars());
if (e1->op == TOKsymoff)
{ SymOffExp *se = (SymOffExp *)e1;
se->var->checkModify(loc, sc, type);
//return toLvalue(sc, e);
}
return Expression::modifiableLvalue(sc, e);
}
#endif
void PtrExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writeByte('*');
expToCBuffer(buf, hgs, e1, precedence[op]);
}
/************************************************************/
NegExp::NegExp(Loc loc, Expression *e)
: UnaExp(loc, TOKneg, sizeof(NegExp), e)
{
}
Expression *NegExp::semantic(Scope *sc)
{ Expression *e;
#if LOGSEMANTIC
printf("NegExp::semantic('%s')\n", toChars());
#endif
if (!type)
{
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e = op_overload(sc);
if (e)
return e;
e1->checkNoBool();
if (!e1->isArrayOperand())
e1->checkArithmetic();
type = e1->type;
}
return this;
}
/************************************************************/
UAddExp::UAddExp(Loc loc, Expression *e)
: UnaExp(loc, TOKuadd, sizeof(UAddExp), e)
{
}
Expression *UAddExp::semantic(Scope *sc)
{ Expression *e;
#if LOGSEMANTIC
printf("UAddExp::semantic('%s')\n", toChars());
#endif
assert(!type);
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e = op_overload(sc);
if (e)
return e;
e1->checkNoBool();
e1->checkArithmetic();
return e1;
}
/************************************************************/
ComExp::ComExp(Loc loc, Expression *e)
: UnaExp(loc, TOKtilde, sizeof(ComExp), e)
{
}
Expression *ComExp::semantic(Scope *sc)
{ Expression *e;
if (!type)
{
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e = op_overload(sc);
if (e)
return e;
e1->checkNoBool();
if (!e1->isArrayOperand())
e1 = e1->checkIntegral();
type = e1->type;
}
return this;
}
/************************************************************/
NotExp::NotExp(Loc loc, Expression *e)
: UnaExp(loc, TOKnot, sizeof(NotExp), e)
{
}
Expression *NotExp::semantic(Scope *sc)
{
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e1 = e1->checkToBoolean();
type = Type::tboolean;
return this;
}
int NotExp::isBit()
{
return TRUE;
}
/************************************************************/
BoolExp::BoolExp(Loc loc, Expression *e, Type *t)
: UnaExp(loc, TOKtobool, sizeof(BoolExp), e)
{
type = t;
}
Expression *BoolExp::semantic(Scope *sc)
{
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e1 = e1->checkToBoolean();
type = Type::tboolean;
return this;
}
int BoolExp::isBit()
{
return TRUE;
}
/************************************************************/
DeleteExp::DeleteExp(Loc loc, Expression *e)
: UnaExp(loc, TOKdelete, sizeof(DeleteExp), e)
{
}
Expression *DeleteExp::semantic(Scope *sc)
{
Type *tb;
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e1 = e1->toLvalue(sc, NULL);
type = Type::tvoid;
tb = e1->type->toBasetype();
switch (tb->ty)
{ case Tclass:
{ TypeClass *tc = (TypeClass *)tb;
ClassDeclaration *cd = tc->sym;
if (cd->isCOMinterface())
{ /* Because COM classes are deleted by IUnknown.Release()
*/
error("cannot delete instance of COM interface %s", cd->toChars());
}
break;
}
case Tpointer:
tb = ((TypePointer *)tb)->next->toBasetype();
if (tb->ty == Tstruct)
{
TypeStruct *ts = (TypeStruct *)tb;
StructDeclaration *sd = ts->sym;
FuncDeclaration *f = sd->aggDelete;
if (f)
{
Type *tpv = Type::tvoid->pointerTo();
Expression *e = e1->castTo(sc, tpv);
Expression *ec = new VarExp(loc, f);
e = new CallExp(loc, ec, e);
return e->semantic(sc);
}
}
break;
case Tarray:
break;
default:
if (e1->op == TOKindex)
{
IndexExp *ae = (IndexExp *)(e1);
Type *tb1 = ae->e1->type->toBasetype();
if (tb1->ty == Taarray)
break;
}
error("cannot delete type %s", e1->type->toChars());
break;
}
if (e1->op == TOKindex)
{
IndexExp *ae = (IndexExp *)(e1);
Type *tb1 = ae->e1->type->toBasetype();
if (tb1->ty == Taarray)
{ if (!global.params.useDeprecated)
error("delete aa[key] deprecated, use aa.remove(key)");
}
}
return this;
}
int DeleteExp::checkSideEffect(int flag)
{
return 1;
}
Expression *DeleteExp::checkToBoolean()
{
error("delete does not give a boolean result");
return this;
}
void DeleteExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("delete ");
expToCBuffer(buf, hgs, e1, precedence[op]);
}
/************************************************************/
CastExp::CastExp(Loc loc, Expression *e, Type *t)
: UnaExp(loc, TOKcast, sizeof(CastExp), e)
{
to = t;
}
#if DMDV2
/* For cast(const) and cast(immutable)
*/
CastExp::CastExp(Loc loc, Expression *e, unsigned mod)
: UnaExp(loc, TOKcast, sizeof(CastExp), e)
{
to = NULL;
this->mod = mod;
}
#endif
Expression *CastExp::syntaxCopy()
{
return new CastExp(loc, e1->syntaxCopy(), to->syntaxCopy());
}
Expression *CastExp::semantic(Scope *sc)
{ Expression *e;
BinExp *b;
UnaExp *u;
#if LOGSEMANTIC
printf("CastExp::semantic('%s')\n", toChars());
#endif
//static int x; assert(++x < 10);
if (type)
return this;
UnaExp::semantic(sc);
if (e1->type) // if not a tuple
{
e1 = resolveProperties(sc, e1);
to = to->semantic(loc, sc);
e = op_overload(sc);
if (e)
{
return e->implicitCastTo(sc, to);
}
if (e1->op == TOKtemplate)
{
error("cannot cast template %s to type %s", e1->toChars(), to->toChars());
return new ErrorExp();
}
Type *t1b = e1->type->toBasetype();
Type *tob = to->toBasetype();
if (tob->ty == Tstruct &&
!tob->equals(t1b) &&
((TypeStruct *)to)->sym->search(0, Id::call, 0)
)
{
/* Look to replace:
* cast(S)t
* with:
* S(t)
*/
// Rewrite as to.call(e1)
e = new TypeExp(loc, to);
e = new DotIdExp(loc, e, Id::call);
e = new CallExp(loc, e, e1);
e = e->semantic(sc);
return e;
}
// Struct casts are possible only when the sizes match
if (tob->ty == Tstruct || t1b->ty == Tstruct)
{
size_t fromsize = t1b->size(loc);
size_t tosize = tob->size(loc);
if (fromsize != tosize)
{
error("cannot cast from %s to %s", e1->type->toChars(), to->toChars());
return new ErrorExp();
}
}
}
if (!e1->type)
{ error("cannot cast %s", e1->toChars());
return new ErrorExp();
}
e = e1->castTo(sc, to);
return e;
}
int CastExp::checkSideEffect(int flag)
{
/* if not:
* cast(void)
* cast(classtype)func()
*/
if (!to->equals(Type::tvoid) &&
!(to->ty == Tclass && e1->op == TOKcall && e1->type->ty == Tclass))
return Expression::checkSideEffect(flag);
return 1;
}
void CastExp::checkEscape()
{ Type *tb = type->toBasetype();
if (tb->ty == Tarray && e1->op == TOKvar &&
e1->type->toBasetype()->ty == Tsarray)
{ VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v)
{
if (!v->isDataseg() && !v->isParameter())
error("escaping reference to local %s", v->toChars());
}
}
}
void CastExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
buf->writestring("cast(");
#if DMDV1
to->toCBuffer(buf, NULL, hgs);
#else
if (to)
to->toCBuffer(buf, NULL, hgs);
else
{
MODtoBuffer(buf, mod);
}
#endif
buf->writeByte(')');
expToCBuffer(buf, hgs, e1, precedence[op]);
}
/************************************************************/
SliceExp::SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr)
: UnaExp(loc, TOKslice, sizeof(SliceExp), e1)
{
this->upr = upr;
this->lwr = lwr;
lengthVar = NULL;
}
Expression *SliceExp::syntaxCopy()
{
Expression *lwr = NULL;
if (this->lwr)
lwr = this->lwr->syntaxCopy();
Expression *upr = NULL;
if (this->upr)
upr = this->upr->syntaxCopy();
return new SliceExp(loc, e1->syntaxCopy(), lwr, upr);
}
Expression *SliceExp::semantic(Scope *sc)
{ Expression *e;
AggregateDeclaration *ad;
//FuncDeclaration *fd;
ScopeDsymbol *sym;
#if LOGSEMANTIC
printf("SliceExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
e = this;
Type *t = e1->type->toBasetype();
if (t->ty == Tpointer)
{
if (!lwr || !upr)
{ error("need upper and lower bound to slice pointer");
return new ErrorExp();
}
}
else if (t->ty == Tarray)
{
}
else if (t->ty == Tsarray)
{
}
else if (t->ty == Tclass)
{
ad = ((TypeClass *)t)->sym;
goto L1;
}
else if (t->ty == Tstruct)
{
ad = ((TypeStruct *)t)->sym;
L1:
if (search_function(ad, Id::slice))
{
// Rewrite as e1.slice(lwr, upr)
e = new DotIdExp(loc, e1, Id::slice);
if (lwr)
{
assert(upr);
e = new CallExp(loc, e, lwr, upr);
}
else
{ assert(!upr);
e = new CallExp(loc, e);
}
e = e->semantic(sc);
return e;
}
goto Lerror;
}
else if (t->ty == Ttuple)
{
if (!lwr && !upr)
return e1;
if (!lwr || !upr)
{ error("need upper and lower bound to slice tuple");
goto Lerror;
}
}
else if (t == Type::terror)
goto Lerr;
else
goto Lerror;
if (t->ty == Tsarray || t->ty == Tarray || t->ty == Ttuple)
{
sym = new ArrayScopeSymbol(this);
sym->loc = loc;
sym->parent = sc->scopesym;
sc = sc->push(sym);
}
if (lwr)
{ lwr = lwr->semantic(sc);
lwr = resolveProperties(sc, lwr);
lwr = lwr->implicitCastTo(sc, Type::tsize_t);
if (lwr->type == Type::terror)
goto Lerr;
}
if (upr)
{ upr = upr->semantic(sc);
upr = resolveProperties(sc, upr);
upr = upr->implicitCastTo(sc, Type::tsize_t);
if (lwr->type == Type::terror)
goto Lerr;
}
if (t->ty == Tsarray || t->ty == Tarray || t->ty == Ttuple)
sc->pop();
if (t->ty == Ttuple)
{
lwr = lwr->optimize(WANTvalue | WANTinterpret);
upr = upr->optimize(WANTvalue | WANTinterpret);
uinteger_t i1 = lwr->toUInteger();
uinteger_t i2 = upr->toUInteger();
size_t length;
TupleExp *te;
TypeTuple *tup;
if (e1->op == TOKtuple) // slicing an expression tuple
{ te = (TupleExp *)e1;
length = te->exps->dim;
}
else if (e1->op == TOKtype) // slicing a type tuple
{ tup = (TypeTuple *)t;
length = Parameter::dim(tup->arguments);
}
else
assert(0);
if (i1 <= i2 && i2 <= length)
{ size_t j1 = (size_t) i1;
size_t j2 = (size_t) i2;
if (e1->op == TOKtuple)
{ Expressions *exps = new Expressions;
exps->setDim(j2 - j1);
for (size_t i = 0; i < j2 - j1; i++)
{ Expression *e = (Expression *)te->exps->data[j1 + i];
exps->data[i] = (void *)e;
}
e = new TupleExp(loc, 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(e1->loc, new TypeTuple(args));
}
e = e->semantic(sc);
}
else
{
error("string slice [%ju .. %ju] is out of bounds", i1, i2);
goto Lerr;
}
return e;
}
if (t->ty == Tarray)
{
type = e1->type;
}
else
type = t->nextOf()->arrayOf();
return e;
Lerror:
if (e1->op == TOKerror)
return e1;
char *s;
if (t->ty == Tvoid)
s = e1->toChars();
else
s = t->toChars();
error("%s cannot be sliced with []", s);
Lerr:
e = new ErrorExp();
return e;
}
void SliceExp::checkEscape()
{
e1->checkEscape();
}
void SliceExp::checkEscapeRef()
{
e1->checkEscapeRef();
}
#if DMDV2
int SliceExp::isLvalue()
{
return 1;
}
#endif
Expression *SliceExp::toLvalue(Scope *sc, Expression *e)
{
return this;
}
Expression *SliceExp::modifiableLvalue(Scope *sc, Expression *e)
{
error("slice expression %s is not a modifiable lvalue", toChars());
return this;
}
void SliceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, precedence[op]);
buf->writeByte('[');
if (upr || lwr)
{
if (lwr)
expToCBuffer(buf, hgs, lwr, PREC_assign);
else
buf->writeByte('0');
buf->writestring("..");
if (upr)
expToCBuffer(buf, hgs, upr, PREC_assign);
else
buf->writestring("length"); // BUG: should be array.length
}
buf->writeByte(']');
}
/********************** ArrayLength **************************************/
ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1)
: UnaExp(loc, TOKarraylength, sizeof(ArrayLengthExp), e1)
{
}
Expression *ArrayLengthExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("ArrayLengthExp::semantic('%s')\n", toChars());
#endif
if (!type)
{
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
type = Type::tsize_t;
}
return this;
}
void ArrayLengthExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writestring(".length");
}
/*********************** ArrayExp *************************************/
// e1 [ i1, i2, i3, ... ]
ArrayExp::ArrayExp(Loc loc, Expression *e1, Expressions *args)
: UnaExp(loc, TOKarray, sizeof(ArrayExp), e1)
{
arguments = args;
}
Expression *ArrayExp::syntaxCopy()
{
return new ArrayExp(loc, e1->syntaxCopy(), arraySyntaxCopy(arguments));
}
Expression *ArrayExp::semantic(Scope *sc)
{ Expression *e;
Type *t1;
#if LOGSEMANTIC
printf("ArrayExp::semantic('%s')\n", toChars());
#endif
UnaExp::semantic(sc);
e1 = resolveProperties(sc, e1);
t1 = e1->type->toBasetype();
if (t1->ty != Tclass && t1->ty != Tstruct)
{ // Convert to IndexExp
if (arguments->dim != 1)
{ error("only one index allowed to index %s", t1->toChars());
goto Lerr;
}
e = new IndexExp(loc, e1, (Expression *)arguments->data[0]);
return e->semantic(sc);
}
// Run semantic() on each argument
for (size_t i = 0; i < arguments->dim; i++)
{ e = (Expression *)arguments->data[i];
e = e->semantic(sc);
if (!e->type)
{ error("%s has no value", e->toChars());
goto Lerr;
}
else if (e->type == Type::terror)
goto Lerr;
arguments->data[i] = (void *)e;
}
expandTuples(arguments);
assert(arguments && arguments->dim);
e = op_overload(sc);
if (!e)
{ error("no [] operator overload for type %s", e1->type->toChars());
goto Lerr;
}
return e;
Lerr:
return new ErrorExp();
}
#if DMDV2
int ArrayExp::isLvalue()
{
if (type && type->toBasetype()->ty == Tvoid)
return 0;
return 1;
}
#endif
Expression *ArrayExp::toLvalue(Scope *sc, Expression *e)
{
if (type && type->toBasetype()->ty == Tvoid)
error("voids have no value");
return this;
}
void ArrayExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{ int i;
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('[');
argsToCBuffer(buf, arguments, hgs);
buf->writeByte(']');
}
/************************* DotExp ***********************************/
DotExp::DotExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKdotexp, sizeof(DotExp), e1, e2)
{
}
Expression *DotExp::semantic(Scope *sc)
{
#if LOGSEMANTIC
printf("DotExp::semantic('%s')\n", toChars());
if (type) printf("\ttype = %s\n", type->toChars());
#endif
e1 = e1->semantic(sc);
e2 = e2->semantic(sc);
if (e2->op == TOKimport)
{
ScopeExp *se = (ScopeExp *)e2;
TemplateDeclaration *td = se->sds->isTemplateDeclaration();
if (td)
{ Expression *e = new DotTemplateExp(loc, e1, td);
e = e->semantic(sc);
return e;
}
}
if (!type)
type = e2->type;
return this;
}
void DotExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
expToCBuffer(buf, hgs, e2, PREC_primary);
}
/************************* CommaExp ***********************************/
CommaExp::CommaExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKcomma, sizeof(CommaExp), e1, e2)
{
}
Expression *CommaExp::semantic(Scope *sc)
{
if (!type)
{ BinExp::semanticp(sc);
type = e2->type;
}
return this;
}
void CommaExp::checkEscape()
{
e2->checkEscape();
}
void CommaExp::checkEscapeRef()
{
e2->checkEscapeRef();
}
#if DMDV2
int CommaExp::isLvalue()
{
return e2->isLvalue();
}
#endif
Expression *CommaExp::toLvalue(Scope *sc, Expression *e)
{
e2 = e2->toLvalue(sc, NULL);
return this;
}
Expression *CommaExp::modifiableLvalue(Scope *sc, Expression *e)
{
e2 = e2->modifiableLvalue(sc, e);
return this;
}
int CommaExp::isBool(int result)
{
return e2->isBool(result);
}
int CommaExp::checkSideEffect(int flag)
{
if (flag == 2)
return e1->checkSideEffect(2) || e2->checkSideEffect(2);
else
{
// Don't check e1 until we cast(void) the a,b code generation
return e2->checkSideEffect(flag);
}
}
/************************** IndexExp **********************************/
// e1 [ e2 ]
IndexExp::IndexExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKindex, sizeof(IndexExp), e1, e2)
{
//printf("IndexExp::IndexExp('%s')\n", toChars());
lengthVar = NULL;
modifiable = 0; // assume it is an rvalue
}
Expression *IndexExp::semantic(Scope *sc)
{ Expression *e;
BinExp *b;
UnaExp *u;
Type *t1;
ScopeDsymbol *sym;
#if LOGSEMANTIC
printf("IndexExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
if (!e1->type)
e1 = e1->semantic(sc);
assert(e1->type); // semantic() should already be run on it
if (e1->op == TOKerror)
goto Lerr;
e = this;
// Note that unlike C we do not implement the int[ptr]
t1 = e1->type->toBasetype();
if (t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Ttuple)
{ // Create scope for 'length' variable
sym = new ArrayScopeSymbol(this);
sym->loc = loc;
sym->parent = sc->scopesym;
sc = sc->push(sym);
}
e2 = e2->semantic(sc);
if (!e2->type)
{
error("%s has no value", e2->toChars());
goto Lerr;
}
e2 = resolveProperties(sc, e2);
if (e2->type == Type::terror)
goto Lerr;
if (t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Ttuple)
sc = sc->pop();
switch (t1->ty)
{
case Tpointer:
case Tarray:
e2 = e2->implicitCastTo(sc, Type::tsize_t);
e->type = t1->next;
break;
case Tsarray:
{
e2 = e2->implicitCastTo(sc, Type::tsize_t);
TypeSArray *tsa = (TypeSArray *)t1;
#if 0 // Don't do now, because it might be short-circuit evaluated
// Do compile time array bounds checking if possible
e2 = e2->optimize(WANTvalue);
if (e2->op == TOKint64)
{
dinteger_t index = e2->toInteger();
dinteger_t length = tsa->dim->toInteger();
if (index < 0 || index >= length)
error("array index [%lld] is outside array bounds [0 .. %lld]",
index, length);
}
#endif
e->type = t1->nextOf();
break;
}
case Taarray:
{ TypeAArray *taa = (TypeAArray *)t1;
e2 = e2->implicitCastTo(sc, taa->index); // type checking
e2 = e2->implicitCastTo(sc, taa->key); // actual argument type
type = taa->next;
break;
}
case Ttuple:
{
e2 = e2->implicitCastTo(sc, Type::tsize_t);
e2 = e2->optimize(WANTvalue);
uinteger_t index = e2->toUInteger();
size_t length;
TupleExp *te;
TypeTuple *tup;
if (e1->op == TOKtuple)
{ te = (TupleExp *)e1;
length = te->exps->dim;
}
else if (e1->op == TOKtype)
{
tup = (TypeTuple *)t1;
length = Parameter::dim(tup->arguments);
}
else
assert(0);
if (index < length)
{
if (e1->op == TOKtuple)
e = (Expression *)te->exps->data[(size_t)index];
else
e = new TypeExp(e1->loc, Parameter::getNth(tup->arguments, (size_t)index)->type);
}
else
{
error("array index [%ju] is outside array bounds [0 .. %zu]",
index, length);
e = e1;
}
break;
}
default:
if (e1->op == TOKerror)
goto Lerr;
error("%s must be an array or pointer type, not %s",
e1->toChars(), e1->type->toChars());
case Terror:
goto Lerr;
}
return e;
Lerr:
return new ErrorExp();
}
#if DMDV2
int IndexExp::isLvalue()
{
return 1;
}
#endif
Expression *IndexExp::toLvalue(Scope *sc, Expression *e)
{
// if (type && type->toBasetype()->ty == Tvoid)
// error("voids have no value");
return this;
}
Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e)
{
//printf("IndexExp::modifiableLvalue(%s)\n", toChars());
modifiable = 1;
if (e1->op == TOKstring)
error("string literals are immutable");
if (e1->type->toBasetype()->ty == Taarray)
e1 = e1->modifiableLvalue(sc, e1);
return toLvalue(sc, e);
}
void IndexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('[');
expToCBuffer(buf, hgs, e2, PREC_assign);
buf->writeByte(']');
}
/************************* PostExp ***********************************/
PostExp::PostExp(enum TOK op, Loc loc, Expression *e)
: BinExp(loc, op, sizeof(PostExp), e,
new IntegerExp(loc, 1, Type::tint32))
{
}
Expression *PostExp::semantic(Scope *sc)
{ Expression *e = this;
if (!type)
{
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
e = this;
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
e1->checkNoBool();
if (e1->type->ty == Tpointer)
e = scaleFactor(sc);
else
e2 = e2->castTo(sc, e1->type);
e->type = e1->type;
}
return e;
}
void PostExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, precedence[op]);
buf->writestring((op == TOKplusplus) ? (char *)"++" : (char *)"--");
}
/************************************************************/
/* op can be TOKassign, TOKconstruct, or TOKblit */
AssignExp::AssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKassign, sizeof(AssignExp), e1, e2)
{
ismemset = 0;
}
Expression *AssignExp::semantic(Scope *sc)
{
Expression *e1old = e1;
#if LOGSEMANTIC
printf("AssignExp::semantic('%s')\n", toChars());
#endif
//printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op));
//printf("e2->op = %d, '%s'\n", e2->op, Token::toChars(e2->op));
if (type)
return this;
if (e2->op == TOKcomma)
{ /* Rewrite to get rid of the comma from rvalue
*/
AssignExp *ea = new AssignExp(loc, e1, ((CommaExp *)e2)->e2);
ea->op = op;
Expression *e = new CommaExp(loc, ((CommaExp *)e2)->e1, ea);
return e->semantic(sc);
}
/* Look for operator overloading of a[i]=value.
* Do it before semantic() otherwise the a[i] will have been
* converted to a.opIndex() already.
*/
if (e1->op == TOKarray)
{
ArrayExp *ae = (ArrayExp *)e1;
AggregateDeclaration *ad;
Identifier *id = Id::index;
ae->e1 = ae->e1->semantic(sc);
Type *t1 = ae->e1->type->toBasetype();
if (t1->ty == Tstruct)
{
ad = ((TypeStruct *)t1)->sym;
goto L1;
}
else if (t1->ty == Tclass)
{
ad = ((TypeClass *)t1)->sym;
L1:
// Rewrite (a[i] = value) to (a.opIndexAssign(value, i))
if (search_function(ad, Id::indexass))
{ Expression *e = new DotIdExp(loc, ae->e1, Id::indexass);
Expressions *a = (Expressions *)ae->arguments->copy();
a->insert(0, e2);
e = new CallExp(loc, e, a);
e = e->semantic(sc);
return e;
}
else
{
// Rewrite (a[i] = value) to (a.opIndex(i, value))
if (search_function(ad, id))
{ Expression *e = new DotIdExp(loc, ae->e1, id);
if (1 || !global.params.useDeprecated)
error("operator [] assignment overload with opIndex(i, value) illegal, use opIndexAssign(value, i)");
e = new CallExp(loc, e, (Expression *)ae->arguments->data[0], e2);
e = e->semantic(sc);
return e;
}
}
}
}
/* Look for operator overloading of a[i..j]=value.
* Do it before semantic() otherwise the a[i..j] will have been
* converted to a.opSlice() already.
*/
if (e1->op == TOKslice)
{ Type *t1;
SliceExp *ae = (SliceExp *)e1;
AggregateDeclaration *ad;
Identifier *id = Id::index;
ae->e1 = ae->e1->semantic(sc);
ae->e1 = resolveProperties(sc, ae->e1);
t1 = ae->e1->type->toBasetype();
if (t1->ty == Tstruct)
{
ad = ((TypeStruct *)t1)->sym;
goto L2;
}
else if (t1->ty == Tclass)
{
ad = ((TypeClass *)t1)->sym;
L2:
// Rewrite (a[i..j] = value) to (a.opIndexAssign(value, i, j))
if (search_function(ad, Id::sliceass))
{ Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass);
Expressions *a = new Expressions();
a->push(e2);
if (ae->lwr)
{ a->push(ae->lwr);
assert(ae->upr);
a->push(ae->upr);
}
else
assert(!ae->upr);
e = new CallExp(loc, e, a);
e = e->semantic(sc);
return e;
}
}
}
Expression *e = BinExp::semantic(sc);
if (e->op == TOKerror)
return e;
if (e1->op == TOKdottd)
{ // Rewrite a.b=e2, when b is a template, as a.b(e2)
Expression *e = new CallExp(loc, e1, e2);
e = e->semantic(sc);
return e;
}
e2 = resolveProperties(sc, e2);
assert(e1->type);
/* Rewrite tuple assignment as a tuple of assignments.
*/
if (e1->op == TOKtuple && e2->op == TOKtuple)
{ TupleExp *tup1 = (TupleExp *)e1;
TupleExp *tup2 = (TupleExp *)e2;
size_t dim = tup1->exps->dim;
if (dim != tup2->exps->dim)
{
error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->dim);
}
else
{ Expressions *exps = new Expressions;
exps->setDim(dim);
for (int i = 0; i < dim; i++)
{ Expression *ex1 = (Expression *)tup1->exps->data[i];
Expression *ex2 = (Expression *)tup2->exps->data[i];
exps->data[i] = (void *) new AssignExp(loc, ex1, ex2);
}
Expression *e = new TupleExp(loc, exps);
e = e->semantic(sc);
return e;
}
}
// Determine if this is an initialization of a reference
int refinit = 0;
if (op == TOKconstruct && e1->op == TOKvar)
{ VarExp *ve = (VarExp *)e1;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v->storage_class & (STCout | STCref))
refinit = 1;
}
Type *t1 = e1->type->toBasetype();
if (t1->ty == Tfunction)
{ // Rewrite f=value to f(value)
Expression *e = new CallExp(loc, e1, e2);
e = e->semantic(sc);
return e;
}
/* If it is an assignment from a 'foreign' type,
* check for operator overloading.
*/
if (t1->ty == Tclass || t1->ty == Tstruct)
{
if (!e2->type->implicitConvTo(e1->type))
{
Expression *e = op_overload(sc);
if (e)
return e;
}
}
e2->rvalue();
if (e1->op == TOKarraylength)
{
// e1 is not an lvalue, but we let code generator handle it
ArrayLengthExp *ale = (ArrayLengthExp *)e1;
ale->e1 = ale->e1->modifiableLvalue(sc, e1);
}
else if (e1->op == TOKslice)
;
else
{ // Try to do a decent error message with the expression
// before it got constant folded
if (op != TOKconstruct)
e1 = e1->modifiableLvalue(sc, e1old);
}
if (e1->op == TOKslice &&
t1->nextOf() &&
e2->implicitConvTo(t1->nextOf())
// !(t1->nextOf()->equals(e2->type->nextOf()))
)
{ // memset
ismemset = 1; // make it easy for back end to tell what this is
e2 = e2->implicitCastTo(sc, t1->next);
}
else if (t1->ty == Tsarray && !refinit)
{
error("cannot assign to static array %s", e1->toChars());
}
else
{
e2 = e2->implicitCastTo(sc, e1->type);
}
/* Look for array operations
*/
if (e1->op == TOKslice && !ismemset &&
(e2->op == TOKadd || e2->op == TOKmin ||
e2->op == TOKmul || e2->op == TOKdiv ||
e2->op == TOKmod || e2->op == TOKxor ||
e2->op == TOKand || e2->op == TOKor ||
e2->op == TOKtilde || e2->op == TOKneg))
{
type = e1->type;
return arrayOp(sc);
}
type = e1->type;
assert(type);
return this;
}
Expression *AssignExp::checkToBoolean()
{
// Things like:
// if (a = b) ...
// are usually mistakes.
error("'=' does not give a boolean result");
return this;
}
/************************************************************/
ConstructExp::ConstructExp(Loc loc, Expression *e1, Expression *e2)
: AssignExp(loc, e1, e2)
{
op = TOKconstruct;
}
/************************************************************/
AddAssignExp::AddAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKaddass, sizeof(AddAssignExp), e1, e2)
{
}
Expression *AddAssignExp::semantic(Scope *sc)
{ Expression *e;
if (type)
return this;
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
Type *tb1 = e1->type->toBasetype();
Type *tb2 = e2->type->toBasetype();
if (e1->op == TOKslice)
{
e = typeCombine(sc);
if (e->op == TOKerror)
return e;
type = e1->type;
return arrayOp(sc);
}
else
{
e1 = e1->modifiableLvalue(sc, e1);
}
if ((tb1->ty == Tarray || tb1->ty == Tsarray) &&
(tb2->ty == Tarray || tb2->ty == Tsarray) &&
tb1->nextOf()->equals(tb2->nextOf())
)
{
type = e1->type;
e = this;
}
else
{
e1->checkScalar();
e1->checkNoBool();
if (tb1->ty == Tpointer && tb2->isintegral())
e = scaleFactor(sc);
else if (tb1->ty == Tbit || tb1->ty == Tbool)
{
#if 0
// Need to rethink this
if (e1->op != TOKvar)
{ // Rewrite e1+=e2 to (v=&e1),*v=*v+e2
VarDeclaration *v;
Expression *ea;
Expression *ex;
Identifier *id = Lexer::uniqueId("__name");
v = new VarDeclaration(loc, tb1->pointerTo(), id, NULL);
v->semantic(sc);
if (!sc->insert(v))
assert(0);
v->parent = sc->func;
ea = new AddrExp(loc, e1);
ea = new AssignExp(loc, new VarExp(loc, v), ea);
ex = new VarExp(loc, v);
ex = new PtrExp(loc, ex);
e = new AddExp(loc, ex, e2);
e = new CastExp(loc, e, e1->type);
e = new AssignExp(loc, ex->syntaxCopy(), e);
e = new CommaExp(loc, ea, e);
}
else
#endif
{ // Rewrite e1+=e2 to e1=e1+e2
// BUG: doesn't account for side effects in e1
// BUG: other assignment operators for bits aren't handled at all
e = new AddExp(loc, e1, e2);
e = new CastExp(loc, e, e1->type);
e = new AssignExp(loc, e1->syntaxCopy(), e);
}
e = e->semantic(sc);
}
else
{
type = e1->type;
typeCombine(sc);
e1->checkArithmetic();
e2->checkArithmetic();
checkComplexAddAssign();
if (type->isreal() || type->isimaginary())
{
assert(global.errors || e2->type->isfloating());
e2 = e2->castTo(sc, e1->type);
}
e = this;
if (e2->type->iscomplex() && !type->iscomplex())
error("Cannot assign %s to %s", e2->type->toChars(), type->toChars());
}
}
return e;
}
/************************************************************/
MinAssignExp::MinAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKminass, sizeof(MinAssignExp), e1, e2)
{
}
Expression *MinAssignExp::semantic(Scope *sc)
{ Expression *e;
if (type)
return this;
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
if (e1->op == TOKslice)
{ // T[] -= ...
e = typeCombine(sc);
if (e->op == TOKerror)
return e;
type = e1->type;
return arrayOp(sc);
}
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
e1->checkNoBool();
if (e1->type->ty == Tpointer && e2->type->isintegral())
e = scaleFactor(sc);
else
{
e1 = e1->checkArithmetic();
e2 = e2->checkArithmetic();
checkComplexAddAssign();
type = e1->type;
typeCombine(sc);
if (type->isreal() || type->isimaginary())
{
assert(e2->type->isfloating());
e2 = e2->castTo(sc, e1->type);
}
e = this;
if (e2->type->iscomplex() && !type->iscomplex())
error("Cannot assign %s to %s", e2->type->toChars(), type->toChars());
}
return e;
}
/************************************************************/
CatAssignExp::CatAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKcatass, sizeof(CatAssignExp), e1, e2)
{
}
Expression *CatAssignExp::semantic(Scope *sc)
{ Expression *e;
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
if (e1->op == TOKslice)
{ SliceExp *se = (SliceExp *)e1;
if (se->e1->type->toBasetype()->ty == Tsarray)
{ error("cannot append to static array %s", se->e1->type->toChars());
return new ErrorExp();
}
}
e1 = e1->modifiableLvalue(sc, e1);
Type *tb1 = e1->type->toBasetype();
Type *tb2 = e2->type->toBasetype();
e2->rvalue();
Type *tb1next = tb1->nextOf();
if ((tb1->ty == Tarray) &&
(tb2->ty == Tarray || tb2->ty == Tsarray) &&
(e2->implicitConvTo(e1->type)
#if DMDV2
|| tb2->nextOf()->implicitConvTo(tb1next)
#endif
)
)
{ // Append array
e2 = e2->castTo(sc, e1->type);
type = e1->type;
e = this;
// Reenable when _d_arrayappendwd and cd are in the runtime.
/* }
else if (tb1->ty == Tarray &&
(tb1next->ty == Tchar || tb1next->ty == Twchar) &&
e2->implicitConvTo(Type::tdchar)
)
{ // Append dchar to char[] or wchar[]
e2 = e2->castTo(sc, Type::tdchar);
type = e1->type;
e = this;
/* Do not allow appending wchar to char[] because if wchar happens
* to be a surrogate pair, nothing good can result.
*/
}
else if ((tb1->ty == Tarray) &&
e2->implicitConvTo(tb1next)
)
{ // Append element
e2 = e2->castTo(sc, tb1next);
type = e1->type;
e = this;
}
else
{
if (tb1 != Type::terror && tb2 != Type::terror)
error("cannot append type %s to type %s", tb2->toChars(), tb1->toChars());
e = new ErrorExp();
}
return e;
}
/************************************************************/
MulAssignExp::MulAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmulass, sizeof(MulAssignExp), e1, e2)
{
}
Expression *MulAssignExp::semantic(Scope *sc)
{ Expression *e;
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
#if DMDV2
if (e1->op == TOKarraylength)
{
e = ArrayLengthExp::rewriteOpAssign(this);
e = e->semantic(sc);
return e;
}
#endif
if (e1->op == TOKslice)
{ // T[] -= ...
e = typeCombine(sc);
if (e->op == TOKerror)
return e;
type = e1->type;
return arrayOp(sc);
}
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
e1->checkNoBool();
type = e1->type;
typeCombine(sc);
e1->checkArithmetic();
e2->checkArithmetic();
checkComplexMulAssign();
if (e2->type->isfloating())
{ Type *t1;
Type *t2;
t1 = e1->type;
t2 = e2->type;
if (t1->isreal())
{
if (t2->isimaginary() || t2->iscomplex())
{
e2 = e2->castTo(sc, t1);
}
}
else if (t1->isimaginary())
{
if (t2->isimaginary() || t2->iscomplex())
{
switch (t1->ty)
{
case Timaginary32: t2 = Type::tfloat32; break;
case Timaginary64: t2 = Type::tfloat64; break;
case Timaginary80: t2 = Type::tfloat80; break;
default:
assert(0);
}
e2 = e2->castTo(sc, t2);
}
}
if (e2->type->iscomplex() && !type->iscomplex())
error("Cannot assign %s to %s", e2->type->toChars(), type->toChars());
}
return this;
}
/************************************************************/
DivAssignExp::DivAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKdivass, sizeof(DivAssignExp), e1, e2)
{
}
Expression *DivAssignExp::semantic(Scope *sc)
{ Expression *e;
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
#if DMDV2
if (e1->op == TOKarraylength)
{
e = ArrayLengthExp::rewriteOpAssign(this);
e = e->semantic(sc);
return e;
}
#endif
if (e1->op == TOKslice)
{ // T[] -= ...
e = typeCombine(sc);
if (e->op == TOKerror)
return e;
type = e1->type;
return arrayOp(sc);
}
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
e1->checkNoBool();
type = e1->type;
typeCombine(sc);
e1->checkArithmetic();
e2->checkArithmetic();
checkComplexMulAssign();
if (e2->type->isimaginary())
{ Type *t1;
Type *t2;
t1 = e1->type;
if (t1->isreal())
{ // x/iv = i(-x/v)
// Therefore, the result is 0
e2 = new CommaExp(loc, e2, new RealExp(loc, 0, t1));
e2->type = t1;
e = new AssignExp(loc, e1, e2);
e->type = t1;
return e;
}
else if (t1->isimaginary())
{ Expression *e;
switch (t1->ty)
{
case Timaginary32: t2 = Type::tfloat32; break;
case Timaginary64: t2 = Type::tfloat64; break;
case Timaginary80: t2 = Type::tfloat80; break;
default:
assert(0);
}
e2 = e2->castTo(sc, t2);
e = new AssignExp(loc, e1, e2);
e->type = t1;
return e;
}
}
if (e2->type->iscomplex() && !type->iscomplex())
error("Cannot assign %s to %s", e2->type->toChars(), type->toChars());
return this;
}
/************************************************************/
ModAssignExp::ModAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmodass, sizeof(ModAssignExp), e1, e2)
{
}
Expression *ModAssignExp::semantic(Scope *sc)
{
BinExp::semantic(sc);
checkComplexMulAssign();
return commonSemanticAssign(sc);
}
/************************************************************/
ShlAssignExp::ShlAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKshlass, sizeof(ShlAssignExp), e1, e2)
{
}
Expression *ShlAssignExp::semantic(Scope *sc)
{ Expression *e;
//printf("ShlAssignExp::semantic()\n");
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
e1->checkNoBool();
type = e1->type;
typeCombine(sc);
e1->checkIntegral();
e2 = e2->checkIntegral();
#if !IN_LLVM
e2 = e2->castTo(sc, Type::tshiftcnt);
#else
e2 = e2->castTo(sc, e1->type);
#endif
return this;
}
/************************************************************/
ShrAssignExp::ShrAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKshrass, sizeof(ShrAssignExp), e1, e2)
{
}
Expression *ShrAssignExp::semantic(Scope *sc)
{ Expression *e;
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
e1->checkNoBool();
type = e1->type;
typeCombine(sc);
e1->checkIntegral();
e2 = e2->checkIntegral();
#if !IN_LLVM
e2 = e2->castTo(sc, Type::tshiftcnt);
#else
e2 = e2->castTo(sc, e1->type);
#endif
return this;
}
/************************************************************/
UshrAssignExp::UshrAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKushrass, sizeof(UshrAssignExp), e1, e2)
{
}
Expression *UshrAssignExp::semantic(Scope *sc)
{ Expression *e;
BinExp::semantic(sc);
e2 = resolveProperties(sc, e2);
e = op_overload(sc);
if (e)
return e;
e1 = e1->modifiableLvalue(sc, e1);
e1->checkScalar();
e1->checkNoBool();
type = e1->type;
typeCombine(sc);
e1->checkIntegral();
e2 = e2->checkIntegral();
#if !IN_LLVM
e2 = e2->castTo(sc, Type::tshiftcnt);
#else
e2 = e2->castTo(sc, e1->type);
#endif
return this;
}
/************************************************************/
AndAssignExp::AndAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKandass, sizeof(AndAssignExp), e1, e2)
{
}
Expression *AndAssignExp::semantic(Scope *sc)
{
return commonSemanticAssignIntegral(sc);
}
/************************************************************/
OrAssignExp::OrAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKorass, sizeof(OrAssignExp), e1, e2)
{
}
Expression *OrAssignExp::semantic(Scope *sc)
{
return commonSemanticAssignIntegral(sc);
}
/************************************************************/
XorAssignExp::XorAssignExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKxorass, sizeof(XorAssignExp), e1, e2)
{
}
Expression *XorAssignExp::semantic(Scope *sc)
{
return commonSemanticAssignIntegral(sc);
}
/************************* AddExp *****************************/
AddExp::AddExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKadd, sizeof(AddExp), e1, e2)
{
}
Expression *AddExp::semantic(Scope *sc)
{ Expression *e;
#if LOGSEMANTIC
printf("AddExp::semantic('%s')\n", toChars());
#endif
if (!type)
{
BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
Type *tb1 = e1->type->toBasetype();
Type *tb2 = e2->type->toBasetype();
if ((tb1->ty == Tarray || tb1->ty == Tsarray) &&
(tb2->ty == Tarray || tb2->ty == Tsarray) &&
tb1->nextOf()->equals(tb2->nextOf())
)
{
type = e1->type;
e = this;
}
else if (tb1->ty == Tpointer && e2->type->isintegral() ||
tb2->ty == Tpointer && e1->type->isintegral())
e = scaleFactor(sc);
else if (tb1->ty == Tpointer && tb2->ty == Tpointer)
{
incompatibleTypes();
type = e1->type;
e = this;
}
else
{
typeCombine(sc);
if ((e1->type->isreal() && e2->type->isimaginary()) ||
(e1->type->isimaginary() && e2->type->isreal()))
{
switch (type->toBasetype()->ty)
{
case Tfloat32:
case Timaginary32:
type = Type::tcomplex32;
break;
case Tfloat64:
case Timaginary64:
type = Type::tcomplex64;
break;
case Tfloat80:
case Timaginary80:
type = Type::tcomplex80;
break;
default:
assert(0);
}
}
e = this;
}
return e;
}
return this;
}
/************************************************************/
MinExp::MinExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmin, sizeof(MinExp), e1, e2)
{
}
Expression *MinExp::semantic(Scope *sc)
{ Expression *e;
Type *t1;
Type *t2;
#if LOGSEMANTIC
printf("MinExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
e = this;
t1 = e1->type->toBasetype();
t2 = e2->type->toBasetype();
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;
Expression *e;
typeCombine(sc); // make sure pointer types are compatible
type = Type::tptrdiff_t;
stride = t2->nextOf()->size();
if (stride == 0)
{
e = new IntegerExp(loc, 0, Type::tptrdiff_t);
}
else
{
e = new DivExp(loc, this, new IntegerExp(0, stride, Type::tptrdiff_t));
e->type = Type::tptrdiff_t;
}
return e;
}
else if (t2->isintegral())
e = scaleFactor(sc);
else
{ error("incompatible types for minus");
return new IntegerExp(0);
}
}
else if (t2->ty == Tpointer)
{
type = e2->type;
error("can't subtract pointer from %s", e1->type->toChars());
return new IntegerExp(0);
}
else
{
typeCombine(sc);
t1 = e1->type->toBasetype();
t2 = e2->type->toBasetype();
if ((t1->isreal() && t2->isimaginary()) ||
(t1->isimaginary() && t2->isreal()))
{
switch (type->ty)
{
case Tfloat32:
case Timaginary32:
type = Type::tcomplex32;
break;
case Tfloat64:
case Timaginary64:
type = Type::tcomplex64;
break;
case Tfloat80:
case Timaginary80:
type = Type::tcomplex80;
break;
default:
assert(0);
}
}
}
return e;
}
/************************* CatExp *****************************/
CatExp::CatExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKcat, sizeof(CatExp), e1, e2)
{
}
Expression *CatExp::semantic(Scope *sc)
{ Expression *e;
//printf("CatExp::semantic() %s\n", toChars());
if (!type)
{
BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
Type *tb1 = e1->type->toBasetype();
Type *tb2 = e2->type->toBasetype();
/* BUG: Should handle things like:
* char c;
* c ~ ' '
* ' ' ~ c;
*/
#if 0
e1->type->print();
e2->type->print();
#endif
if ((tb1->ty == Tsarray || tb1->ty == Tarray) &&
e2->type->equals(tb1->next))
{
type = tb1->nextOf()->arrayOf();
if (tb2->ty == Tarray)
{ // Make e2 into [e2]
e2 = new ArrayLiteralExp(e2->loc, e2);
e2->type = type;
}
return this;
}
else if ((tb2->ty == Tsarray || tb2->ty == Tarray) &&
e1->type->equals(tb2->next))
{
type = tb2->nextOf()->arrayOf();
if (tb1->ty == Tarray)
{ // Make e1 into [e1]
e1 = new ArrayLiteralExp(e1->loc, e1);
e1->type = type;
}
return this;
}
typeCombine(sc);
if (type->toBasetype()->ty == Tsarray)
type = type->toBasetype()->next->arrayOf();
#if 0
e1->type->print();
e2->type->print();
type->print();
print();
#endif
if (e1->op == TOKstring && e2->op == TOKstring)
e = optimize(WANTvalue);
else if (e1->type->equals(e2->type) &&
(e1->type->toBasetype()->ty == Tarray ||
e1->type->toBasetype()->ty == Tsarray))
{
e = this;
}
else
{
//printf("(%s) ~ (%s)\n", e1->toChars(), e2->toChars());
error("Can only concatenate arrays, not (%s ~ %s)",
e1->type->toChars(), e2->type->toChars());
type = Type::tint32;
e = this;
}
e->type = e->type->semantic(loc, sc);
return e;
}
return this;
}
/************************************************************/
MulExp::MulExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmul, sizeof(MulExp), e1, e2)
{
}
Expression *MulExp::semantic(Scope *sc)
{ Expression *e;
#if 0
printf("MulExp::semantic() %s\n", toChars());
#endif
if (type)
{
return this;
}
BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
typeCombine(sc);
if (!e1->isArrayOperand())
e1->checkArithmetic();
if (!e2->isArrayOperand())
e2->checkArithmetic();
if (type->isfloating())
{ Type *t1 = e1->type;
Type *t2 = e2->type;
if (t1->isreal())
{
type = t2;
}
else if (t2->isreal())
{
type = t1;
}
else if (t1->isimaginary())
{
if (t2->isimaginary())
{ Expression *e;
switch (t1->ty)
{
case Timaginary32: type = Type::tfloat32; break;
case Timaginary64: type = Type::tfloat64; break;
case Timaginary80: type = Type::tfloat80; break;
default: assert(0);
}
// iy * iv = -yv
e1->type = type;
e2->type = type;
e = new NegExp(loc, this);
e = e->semantic(sc);
return e;
}
else
type = t2; // t2 is complex
}
else if (t2->isimaginary())
{
type = t1; // t1 is complex
}
}
return this;
}
/************************************************************/
DivExp::DivExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKdiv, sizeof(DivExp), e1, e2)
{
}
Expression *DivExp::semantic(Scope *sc)
{ Expression *e;
if (type)
return this;
BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
typeCombine(sc);
if (!e1->isArrayOperand())
e1->checkArithmetic();
if (!e2->isArrayOperand())
e2->checkArithmetic();
if (type->isfloating())
{ Type *t1 = e1->type;
Type *t2 = e2->type;
if (t1->isreal())
{
type = t2;
if (t2->isimaginary())
{ Expression *e;
// x/iv = i(-x/v)
e2->type = t1;
e = new NegExp(loc, this);
e = e->semantic(sc);
return e;
}
}
else if (t2->isreal())
{
type = t1;
}
else if (t1->isimaginary())
{
if (t2->isimaginary())
{
switch (t1->ty)
{
case Timaginary32: type = Type::tfloat32; break;
case Timaginary64: type = Type::tfloat64; break;
case Timaginary80: type = Type::tfloat80; break;
default: assert(0);
}
}
else
type = t2; // t2 is complex
}
else if (t2->isimaginary())
{
type = t1; // t1 is complex
}
}
return this;
}
/************************************************************/
ModExp::ModExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKmod, sizeof(ModExp), e1, e2)
{
}
Expression *ModExp::semantic(Scope *sc)
{ Expression *e;
if (type)
return this;
BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
typeCombine(sc);
if (!e1->isArrayOperand())
e1->checkArithmetic();
if (!e2->isArrayOperand())
e2->checkArithmetic();
if (type->isfloating())
{ type = e1->type;
if (e2->type->iscomplex())
{ error("cannot perform modulo complex arithmetic");
return new IntegerExp(0);
}
}
return this;
}
/************************************************************/
ShlExp::ShlExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKshl, sizeof(ShlExp), e1, e2)
{
}
Expression *ShlExp::semantic(Scope *sc)
{ Expression *e;
//printf("ShlExp::semantic(), type = %p\n", type);
if (!type)
{ BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
e1 = e1->checkIntegral();
e2 = e2->checkIntegral();
e1 = e1->integralPromotions(sc);
#if !IN_LLVM
e2 = e2->castTo(sc, Type::tshiftcnt);
#else
e2 = e2->castTo(sc, e1->type);
#endif
type = e1->type;
}
return this;
}
/************************************************************/
ShrExp::ShrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKshr, sizeof(ShrExp), e1, e2)
{
}
Expression *ShrExp::semantic(Scope *sc)
{ Expression *e;
if (!type)
{ BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
e1 = e1->checkIntegral();
e2 = e2->checkIntegral();
e1 = e1->integralPromotions(sc);
#if !IN_LLVM
e2 = e2->castTo(sc, Type::tshiftcnt);
#else
e2 = e2->castTo(sc, e1->type);
#endif
type = e1->type;
}
return this;
}
/************************************************************/
UshrExp::UshrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKushr, sizeof(UshrExp), e1, e2)
{
}
Expression *UshrExp::semantic(Scope *sc)
{ Expression *e;
if (!type)
{ BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
e1 = e1->checkIntegral();
e2 = e2->checkIntegral();
e1 = e1->integralPromotions(sc);
#if !IN_LLVM
e2 = e2->castTo(sc, Type::tshiftcnt);
#else
e2 = e2->castTo(sc, e1->type);
#endif
type = e1->type;
}
return this;
}
/************************************************************/
AndExp::AndExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKand, sizeof(AndExp), e1, e2)
{
}
Expression *AndExp::semantic(Scope *sc)
{ Expression *e;
if (!type)
{ BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
if (e1->type->toBasetype()->ty == Tbool &&
e2->type->toBasetype()->ty == Tbool)
{
type = e1->type;
e = this;
}
else
{
typeCombine(sc);
if (!e1->isArrayOperand())
e1->checkIntegral();
if (!e2->isArrayOperand())
e2->checkIntegral();
}
}
return this;
}
/************************************************************/
OrExp::OrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKor, sizeof(OrExp), e1, e2)
{
}
Expression *OrExp::semantic(Scope *sc)
{ Expression *e;
if (!type)
{ BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
if (e1->type->toBasetype()->ty == Tbool &&
e2->type->toBasetype()->ty == Tbool)
{
type = e1->type;
e = this;
}
else
{
typeCombine(sc);
if (!e1->isArrayOperand())
e1->checkIntegral();
if (!e2->isArrayOperand())
e2->checkIntegral();
}
}
return this;
}
/************************************************************/
XorExp::XorExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKxor, sizeof(XorExp), e1, e2)
{
}
Expression *XorExp::semantic(Scope *sc)
{ Expression *e;
if (!type)
{ BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
if (e1->type->toBasetype()->ty == Tbool &&
e2->type->toBasetype()->ty == Tbool)
{
type = e1->type;
e = this;
}
else
{
typeCombine(sc);
if (!e1->isArrayOperand())
e1->checkIntegral();
if (!e2->isArrayOperand())
e2->checkIntegral();
}
}
return this;
}
/************************************************************/
OrOrExp::OrOrExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKoror, sizeof(OrOrExp), e1, e2)
{
}
Expression *OrOrExp::semantic(Scope *sc)
{
unsigned cs1;
// same as for AndAnd
e1 = e1->semantic(sc);
e1 = resolveProperties(sc, e1);
e1 = e1->checkToPointer();
e1 = e1->checkToBoolean();
cs1 = sc->callSuper;
if (sc->flags & SCOPEstaticif)
{
/* If in static if, don't evaluate e2 if we don't have to.
*/
e1 = e1->optimize(WANTflags);
if (e1->isBool(TRUE))
{
return new IntegerExp(loc, 1, Type::tboolean);
}
}
e2 = e2->semantic(sc);
sc->mergeCallSuper(loc, cs1);
e2 = resolveProperties(sc, e2);
e2 = e2->checkToPointer();
type = Type::tboolean;
if (e2->type->ty == Tvoid)
type = Type::tvoid;
if (e2->op == TOKtype || e2->op == TOKimport)
error("%s is not an expression", e2->toChars());
return this;
}
Expression *OrOrExp::checkToBoolean()
{
e2 = e2->checkToBoolean();
return this;
}
int OrOrExp::isBit()
{
return TRUE;
}
int OrOrExp::checkSideEffect(int flag)
{
if (flag == 2)
{
return e1->checkSideEffect(2) || e2->checkSideEffect(2);
}
else
{ e1->checkSideEffect(1);
return e2->checkSideEffect(flag);
}
}
/************************************************************/
AndAndExp::AndAndExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKandand, sizeof(AndAndExp), e1, e2)
{
}
Expression *AndAndExp::semantic(Scope *sc)
{
unsigned cs1;
// same as for OrOr
e1 = e1->semantic(sc);
e1 = resolveProperties(sc, e1);
e1 = e1->checkToPointer();
e1 = e1->checkToBoolean();
cs1 = sc->callSuper;
if (sc->flags & SCOPEstaticif)
{
/* If in static if, don't evaluate e2 if we don't have to.
*/
e1 = e1->optimize(WANTflags);
if (e1->isBool(FALSE))
{
return new IntegerExp(loc, 0, Type::tboolean);
}
}
e2 = e2->semantic(sc);
sc->mergeCallSuper(loc, cs1);
e2 = resolveProperties(sc, e2);
e2 = e2->checkToPointer();
type = Type::tboolean;
if (e2->type->ty == Tvoid)
type = Type::tvoid;
if (e2->op == TOKtype || e2->op == TOKimport)
error("%s is not an expression", e2->toChars());
return this;
}
Expression *AndAndExp::checkToBoolean()
{
e2 = e2->checkToBoolean();
return this;
}
int AndAndExp::isBit()
{
return TRUE;
}
int AndAndExp::checkSideEffect(int flag)
{
if (flag == 2)
{
return e1->checkSideEffect(2) || e2->checkSideEffect(2);
}
else
{
e1->checkSideEffect(1);
return e2->checkSideEffect(flag);
}
}
/************************************************************/
InExp::InExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKin, sizeof(InExp), e1, e2)
{
}
Expression *InExp::semantic(Scope *sc)
{ Expression *e;
if (type)
return this;
BinExp::semanticp(sc);
e = op_overload(sc);
if (e)
return e;
//type = Type::tboolean;
Type *t2b = e2->type->toBasetype();
switch (t2b->ty)
{
case Taarray:
{
TypeAArray *ta = (TypeAArray *)t2b;
#if DMDV2
// Special handling for array keys
if (!arrayTypeCompatible(e1->loc, e1->type, ta->index))
#endif
{
// Convert key to type of key
e1 = e1->implicitCastTo(sc, ta->index);
}
// Return type is pointer to value
type = ta->nextOf()->pointerTo();
break;
}
default:
error("rvalue of in expression must be an associative array, not %s", e2->type->toChars());
case Terror:
return new ErrorExp();
}
return this;
}
int InExp::isBit()
{
return FALSE;
}
/************************************************************/
/* This deletes the key e1 from the associative array e2
*/
RemoveExp::RemoveExp(Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, TOKremove, sizeof(RemoveExp), e1, e2)
{
type = Type::tvoid;
}
void RemoveExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writestring(".remove(");
expToCBuffer(buf, hgs, e2, PREC_assign);
buf->writestring(")");
}
/************************************************************/
CmpExp::CmpExp(enum TOK op, Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, op, sizeof(CmpExp), e1, e2)
{
}
Expression *CmpExp::semantic(Scope *sc)
{ Expression *e;
#if LOGSEMANTIC
printf("CmpExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
BinExp::semanticp(sc);
Type *t1 = e1->type->toBasetype();
Type *t2 = e2->type->toBasetype();
if (t1->ty == Tclass && e2->op == TOKnull ||
t2->ty == Tclass && e1->op == TOKnull)
{
error("do not use null when comparing class types");
}
e = op_overload(sc);
if (e)
{
if (!e->type->isscalar() && e->type->equals(e1->type))
{
error("recursive opCmp expansion");
e = new ErrorExp();
}
else
{ e = new CmpExp(op, loc, e, new IntegerExp(loc, 0, Type::tint32));
e = e->semantic(sc);
}
return e;
}
/* Disallow comparing T[]==T and T==T[]
*/
if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) ||
e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf()))
{
incompatibleTypes();
return new ErrorExp();
}
typeCombine(sc);
type = Type::tboolean;
// Special handling for array comparisons
t1 = e1->type->toBasetype();
t2 = e2->type->toBasetype();
if ((t1->ty == Tarray || t1->ty == Tsarray) &&
(t2->ty == Tarray || t2->ty == Tsarray))
{
if (!t1->next->equals(t2->next))
error("array comparison type mismatch, %s vs %s", t1->next->toChars(), t2->next->toChars());
e = this;
}
else if (t1->ty == Tstruct || t2->ty == Tstruct ||
(t1->ty == Tclass && t2->ty == Tclass))
{
if (t2->ty == Tstruct)
error("need member function opCmp() for %s %s to compare", t2->toDsymbol(sc)->kind(), t2->toChars());
else
error("need member function opCmp() for %s %s to compare", t1->toDsymbol(sc)->kind(), t1->toChars());
e = this;
}
#if 1
else if (t1->iscomplex() || t2->iscomplex())
{
error("compare not defined for complex operands");
e = new IntegerExp(0);
}
#endif
else
e = this;
//printf("CmpExp: %s\n", e->toChars());
return e;
}
int CmpExp::isBit()
{
return TRUE;
}
/************************************************************/
EqualExp::EqualExp(enum TOK op, Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, op, sizeof(EqualExp), e1, e2)
{
assert(op == TOKequal || op == TOKnotequal);
}
Expression *EqualExp::semantic(Scope *sc)
{ Expression *e;
//printf("EqualExp::semantic('%s')\n", toChars());
if (type)
return this;
BinExp::semanticp(sc);
/* 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 (e1->op == TOKaddress && e2->op == TOKaddress)
{ AddrExp *ae1 = (AddrExp *)e1;
AddrExp *ae2 = (AddrExp *)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 /*|| ve1->var->toSymbol() == ve2->var->toSymbol()*/)
{
// They are the same, result is 'true' for ==, 'false' for !=
e = new IntegerExp(loc, (op == TOKequal), Type::tboolean);
return e;
}
}
}
Type *t1 = e1->type->toBasetype();
Type *t2 = e2->type->toBasetype();
if (t1->ty == Tclass && e2->op == TOKnull ||
t2->ty == Tclass && e1->op == TOKnull)
{
error("use '%s' instead of '%s' when comparing with null",
Token::toChars(op == TOKequal ? TOKidentity : TOKnotidentity),
Token::toChars(op));
}
//if (e2->op != TOKnull)
{
e = op_overload(sc);
if (e)
{
if (op == TOKnotequal)
{
e = new NotExp(e->loc, e);
e = e->semantic(sc);
}
return e;
}
}
/* Disallow comparing T[]==T and T==T[]
*/
if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) ||
e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf()))
{
incompatibleTypes();
return new ErrorExp();
}
e = typeCombine(sc);
type = Type::tboolean;
// Special handling for array comparisons
t1 = e1->type->toBasetype();
t2 = e2->type->toBasetype();
if ((t1->ty == Tarray || t1->ty == Tsarray) &&
(t2->ty == Tarray || t2->ty == Tsarray))
{
if (!t1->next->equals(t2->next))
error("array comparison type mismatch, %s vs %s", t1->next->toChars(), t2->next->toChars());
}
else
{
if (e1->type != e2->type && e1->type->isfloating() && e2->type->isfloating())
{
// Cast both to complex
e1 = e1->castTo(sc, Type::tcomplex80);
e2 = e2->castTo(sc, Type::tcomplex80);
}
}
return e;
}
int EqualExp::isBit()
{
return TRUE;
}
/************************************************************/
IdentityExp::IdentityExp(enum TOK op, Loc loc, Expression *e1, Expression *e2)
: BinExp(loc, op, sizeof(IdentityExp), e1, e2)
{
}
Expression *IdentityExp::semantic(Scope *sc)
{
if (type)
return this;
BinExp::semanticp(sc);
type = Type::tboolean;
typeCombine(sc);
if (e1->type != e2->type && e1->type->isfloating() && e2->type->isfloating())
{
// Cast both to complex
e1 = e1->castTo(sc, Type::tcomplex80);
e2 = e2->castTo(sc, Type::tcomplex80);
}
return this;
}
int IdentityExp::isBit()
{
return TRUE;
}
/****************************************************************/
CondExp::CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2)
: BinExp(loc, TOKquestion, sizeof(CondExp), e1, e2)
{
this->econd = econd;
}
Expression *CondExp::syntaxCopy()
{
return new CondExp(loc, econd->syntaxCopy(), e1->syntaxCopy(), e2->syntaxCopy());
}
Expression *CondExp::semantic(Scope *sc)
{ Type *t1;
Type *t2;
unsigned cs0;
unsigned cs1;
#if LOGSEMANTIC
printf("CondExp::semantic('%s')\n", toChars());
#endif
if (type)
return this;
econd = econd->semantic(sc);
econd = resolveProperties(sc, econd);
econd = econd->checkToPointer();
econd = econd->checkToBoolean();
#if 0 /* this cannot work right because the types of e1 and e2
* both contribute to the type of the result.
*/
if (sc->flags & SCOPEstaticif)
{
/* If in static if, don't evaluate what we don't have to.
*/
econd = econd->optimize(WANTflags);
if (econd->isBool(TRUE))
{
e1 = e1->semantic(sc);
e1 = resolveProperties(sc, e1);
return e1;
}
else if (econd->isBool(FALSE))
{
e2 = e2->semantic(sc);
e2 = resolveProperties(sc, e2);
return e2;
}
}
#endif
cs0 = sc->callSuper;
e1 = e1->semantic(sc);
e1 = resolveProperties(sc, e1);
cs1 = sc->callSuper;
sc->callSuper = cs0;
e2 = e2->semantic(sc);
e2 = resolveProperties(sc, e2);
sc->mergeCallSuper(loc, cs1);
// If either operand is void, the result is void
t1 = e1->type;
t2 = e2->type;
if (t1->ty == Tvoid || t2->ty == Tvoid)
type = Type::tvoid;
else if (t1 == t2)
type = t1;
else
{
typeCombine(sc);
switch (e1->type->toBasetype()->ty)
{
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
e2 = e2->castTo(sc, e1->type);
break;
}
switch (e2->type->toBasetype()->ty)
{
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
e1 = e1->castTo(sc, e2->type);
break;
}
}
#if 0
printf("res: %s\n", type->toChars());
printf("e1 : %s\n", e1->type->toChars());
printf("e2 : %s\n", e2->type->toChars());
#endif
return this;
}
#if DMDV2
int CondExp::isLvalue()
{
return e1->isLvalue() && e2->isLvalue();
}
#endif
Expression *CondExp::toLvalue(Scope *sc, Expression *ex)
{
PtrExp *e;
// convert (econd ? e1 : e2) to *(econd ? &e1 : &e2)
e = new PtrExp(loc, this, type);
e1 = e1->addressOf(sc);
//e1 = e1->toLvalue(sc, NULL);
e2 = e2->addressOf(sc);
//e2 = e2->toLvalue(sc, NULL);
typeCombine(sc);
type = e2->type;
return e;
}
Expression *CondExp::modifiableLvalue(Scope *sc, Expression *e)
{
error("conditional expression %s is not a modifiable lvalue", toChars());
return this;
}
void CondExp::checkEscape()
{
e1->checkEscape();
e2->checkEscape();
}
void CondExp::checkEscapeRef()
{
e1->checkEscapeRef();
e2->checkEscapeRef();
}
Expression *CondExp::checkToBoolean()
{
e1 = e1->checkToBoolean();
e2 = e2->checkToBoolean();
return this;
}
int CondExp::checkSideEffect(int flag)
{
if (flag == 2)
{
return econd->checkSideEffect(2) ||
e1->checkSideEffect(2) ||
e2->checkSideEffect(2);
}
else
{
econd->checkSideEffect(1);
e1->checkSideEffect(flag);
return e2->checkSideEffect(flag);
}
}
#if DMDV2
int CondExp::canThrow()
{
return econd->canThrow() || e1->canThrow() || e2->canThrow();
}
#endif
void CondExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, econd, PREC_oror);
buf->writestring(" ? ");
expToCBuffer(buf, hgs, e1, PREC_expr);
buf->writestring(" : ");
expToCBuffer(buf, hgs, e2, PREC_cond);
}
/************************************************************/
#if IN_LLVM
// Strictly LDC specific stuff
GEPExp::GEPExp(Loc loc, Expression* e, Identifier* id, unsigned idx)
: UnaExp(loc, TOKgep, sizeof(GEPExp), e)
{
index = idx;
ident = id;
}
void GEPExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
expToCBuffer(buf, hgs, e1, PREC_primary);
buf->writeByte('.');
buf->writestring(ident->toChars());
}
Expression* GEPExp::toLvalue(Scope* sc, Expression* e)
{
// GEP's are always lvalues, at least in the "LLVM sense" ...
return this;
}
#endif