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