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