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