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