| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| */ |
| |
| #include "root/dsystem.h" |
| #include "root/rmem.h" |
| #include "root/root.h" |
| |
| #include "mars.h" |
| #include "mangle.h" |
| #include "mtype.h" |
| #include "init.h" |
| #include "expression.h" |
| #include "template.h" |
| #include "utf.h" |
| #include "enum.h" |
| #include "scope.h" |
| #include "statement.h" |
| #include "declaration.h" |
| #include "aggregate.h" |
| #include "import.h" |
| #include "id.h" |
| #include "dsymbol.h" |
| #include "module.h" |
| #include "attrib.h" |
| #include "hdrgen.h" |
| #include "parse.h" |
| #include "nspace.h" |
| #include "ctfe.h" |
| #include "target.h" |
| |
| bool typeMerge(Scope *sc, TOK op, Type **pt, Expression **pe1, Expression **pe2); |
| bool isArrayOpValid(Expression *e); |
| Expression *expandVar(int result, VarDeclaration *v); |
| TypeTuple *toArgTypes(Type *t); |
| bool checkAssignEscape(Scope *sc, Expression *e, bool gag); |
| bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag); |
| bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember); |
| bool checkNestedRef(Dsymbol *s, Dsymbol *p); |
| bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t istart = 0); |
| bool symbolIsVisible(Module *mod, Dsymbol *s); |
| VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); |
| Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false); |
| Type *getTypeInfoType(Loc loc, Type *t, Scope *sc); |
| bool MODimplicitConv(MOD modfrom, MOD modto); |
| MATCH MODmethodConv(MOD modfrom, MOD modto); |
| void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod); |
| |
| void unSpeculative(Scope *sc, RootObject *o); |
| bool arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt); |
| bool checkDefCtor(Loc loc, Type *t); |
| bool isDotOpDispatch(Expression *e); |
| bool functionParameters(Loc loc, Scope *sc, TypeFunction *tf, Type *tthis, Expressions *arguments, FuncDeclaration *fd, Type **prettype, Expression **peprefix); |
| Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad, Expression *e1, Declaration *var, int flag = 0); |
| bool isNeedThisScope(Scope *sc, Declaration *d); |
| Expression *resolveUFCS(Scope *sc, CallExp *ce); |
| bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg); |
| bool isSafeCast(Expression *e, Type *tfrom, Type *tto); |
| FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL); |
| Expression *callCpCtor(Scope *sc, Expression *e); |
| |
| Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads); |
| Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NULL); |
| Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL); |
| Expression *trySemantic(Expression *e, Scope *sc); |
| Expression *unaSemantic(UnaExp *e, Scope *sc); |
| Expression *binSemantic(BinExp *e, Scope *sc); |
| Expression *binSemanticProp(BinExp *e, Scope *sc); |
| Expression *semantic(Expression *e, Scope *sc); |
| Expression *semanticY(DotIdExp *exp, Scope *sc, int flag); |
| Expression *semanticY(DotTemplateInstanceExp *exp, Scope *sc, int flag); |
| StringExp *semanticString(Scope *sc, Expression *exp, const char *s); |
| |
| /**************************************** |
| * Preprocess arguments to function. |
| * Output: |
| * exps[] tuples expanded, properties resolved, rewritten in place |
| * Returns: |
| * true a semantic error occurred |
| */ |
| |
| static bool preFunctionParameters(Scope *sc, Expressions *exps) |
| { |
| bool err = false; |
| if (exps) |
| { |
| expandTuples(exps); |
| |
| for (size_t i = 0; i < exps->dim; i++) |
| { |
| Expression *arg = (*exps)[i]; |
| |
| arg = resolveProperties(sc, arg); |
| if (arg->op == TOKtype) |
| { |
| arg->error("cannot pass type %s as a function argument", arg->toChars()); |
| arg = new ErrorExp(); |
| err = true; |
| } |
| else if (arg->type->toBasetype()->ty == Tfunction) |
| { |
| arg->error("cannot pass type %s as a function argument", arg->toChars()); |
| arg = new ErrorExp(); |
| err = true; |
| } |
| else if (checkNonAssignmentArrayOp(arg)) |
| { |
| arg = new ErrorExp(); |
| err = true; |
| } |
| (*exps)[i] = arg; |
| } |
| } |
| return err; |
| } |
| |
| class ExpressionSemanticVisitor : public Visitor |
| { |
| public: |
| Expression *result; |
| Scope *sc; |
| |
| ExpressionSemanticVisitor(Scope *sc) |
| { |
| this->result = NULL; |
| this->sc = sc; |
| } |
| |
| private: |
| void setError() |
| { |
| result = new ErrorExp(); |
| } |
| |
| /********************* |
| * Mark the operand as will never be dereferenced, |
| * which is useful info for @safe checks. |
| * Do before semantic() on operands rewrites them. |
| */ |
| static void setNoderefOperand(UnaExp *e) |
| { |
| if (e->e1->op == TOKdotid) |
| ((DotIdExp *)e->e1)->noderef = true; |
| } |
| |
| /********************* |
| * Mark the operands as will never be dereferenced, |
| * which is useful info for @safe checks. |
| * Do before semantic() on operands rewrites them. |
| */ |
| static void setNoderefOperands(BinExp *e) |
| { |
| if (e->e1->op == TOKdotid) |
| ((DotIdExp *)e->e1)->noderef = true; |
| if (e->e2->op == TOKdotid) |
| ((DotIdExp *)e->e2)->noderef = true; |
| } |
| |
| static FuncDeclaration *resolveOverloadSet(Loc loc, Scope *sc, |
| OverloadSet *os, Objects* tiargs, Type *tthis, Expressions *arguments) |
| { |
| FuncDeclaration *f = NULL; |
| for (size_t i = 0; i < os->a.dim; i++) |
| { |
| Dsymbol *s = os->a[i]; |
| if (tiargs && s->isFuncDeclaration()) |
| continue; |
| if (FuncDeclaration *f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, 1)) |
| { |
| if (f2->errors) |
| return NULL; |
| if (f) |
| { |
| /* Error if match in more than one overload set, |
| * even if one is a 'better' match than the other. |
| */ |
| ScopeDsymbol::multiplyDefined(loc, f, f2); |
| } |
| else |
| f = f2; |
| } |
| } |
| if (!f) |
| ::error(loc, "no overload matches for %s", os->toChars()); |
| else if (f->errors) |
| f = NULL; |
| return f; |
| } |
| |
| /**************************************************** |
| * Determine if `exp`, which takes the address of `v`, can do so safely. |
| * Params: |
| * sc = context |
| * exp = expression that takes the address of `v` |
| * v = the variable getting its address taken |
| * Returns: |
| * `true` if ok, `false` for error |
| */ |
| static bool checkAddressVar(Scope *sc, UnaExp *e, VarDeclaration *v) |
| { |
| if (v) |
| { |
| if (!v->canTakeAddressOf()) |
| { |
| e->error("cannot take address of %s", e->e1->toChars()); |
| return false; |
| } |
| if (sc->func && !sc->intypeof && !v->isDataseg()) |
| { |
| const char *p = v->isParameter() ? "parameter" : "local"; |
| if (global.params.vsafe) |
| { |
| // Taking the address of v means it cannot be set to 'scope' later |
| v->storage_class &= ~STCmaybescope; |
| v->doNotInferScope = true; |
| if (v->storage_class & STCscope && sc->func->setUnsafe()) |
| { |
| e->error("cannot take address of scope %s %s in @safe function %s", p, v->toChars(), sc->func->toChars()); |
| return false; |
| } |
| } |
| else if (sc->func->setUnsafe()) |
| { |
| e->error("cannot take address of %s %s in @safe function %s", p, v->toChars(), sc->func->toChars()); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| static bool checkVectorElem(Expression *e, Expression *elem) |
| { |
| if (elem->isConst() == 1) |
| return false; |
| |
| e->error("constant expression expected, not %s", elem->toChars()); |
| return true; |
| } |
| |
| public: |
| void visit(Expression *e) |
| { |
| if (e->type) |
| e->type = e->type->semantic(e->loc, sc); |
| else |
| e->type = Type::tvoid; |
| result = e; |
| } |
| |
| void visit(IntegerExp *e) |
| { |
| assert(e->type); |
| if (e->type->ty == Terror) |
| return setError(); |
| assert(e->type->deco); |
| e->normalize(); |
| result = e; |
| } |
| |
| void visit(RealExp *e) |
| { |
| if (!e->type) |
| e->type = Type::tfloat64; |
| else |
| e->type = e->type->semantic(e->loc, sc); |
| result = e; |
| } |
| |
| void visit(ComplexExp *e) |
| { |
| if (!e->type) |
| e->type = Type::tcomplex80; |
| else |
| e->type = e->type->semantic(e->loc, sc); |
| result = e; |
| } |
| |
| void visit(IdentifierExp *exp) |
| { |
| if (exp->type) // This is used as the dummy expression |
| { |
| result = exp; |
| return; |
| } |
| |
| Dsymbol *scopesym; |
| Dsymbol *s = sc->search(exp->loc, exp->ident, &scopesym); |
| if (s) |
| { |
| if (s->errors) |
| return setError(); |
| |
| Expression *e; |
| |
| /* See if the symbol was a member of an enclosing 'with' |
| */ |
| WithScopeSymbol *withsym = scopesym->isWithScopeSymbol(); |
| if (withsym && withsym->withstate->wthis) |
| { |
| /* Disallow shadowing |
| */ |
| // First find the scope of the with |
| Scope *scwith = sc; |
| while (scwith->scopesym != scopesym) |
| { |
| scwith = scwith->enclosing; |
| assert(scwith); |
| } |
| // Look at enclosing scopes for symbols with the same name, |
| // in the same function |
| for (Scope *scx = scwith; scx && scx->func == scwith->func; scx = scx->enclosing) |
| { |
| Dsymbol *s2; |
| if (scx->scopesym && scx->scopesym->symtab && |
| (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL && |
| s != s2) |
| { |
| exp->error("with symbol %s is shadowing local symbol %s", s->toPrettyChars(), s2->toPrettyChars()); |
| return setError(); |
| } |
| } |
| s = s->toAlias(); |
| |
| // Same as wthis.ident |
| // TODO: DotIdExp.semantic will find 'ident' from 'wthis' again. |
| // The redudancy should be removed. |
| e = new VarExp(exp->loc, withsym->withstate->wthis); |
| e = new DotIdExp(exp->loc, e, exp->ident); |
| e = semantic(e, sc); |
| } |
| else |
| { |
| if (withsym) |
| { |
| Declaration *d = s->isDeclaration(); |
| if (d) |
| checkAccess(exp->loc, sc, NULL, d); |
| } |
| |
| /* If f is really a function template, |
| * then replace f with the function template declaration. |
| */ |
| FuncDeclaration *f = s->isFuncDeclaration(); |
| if (f) |
| { |
| TemplateDeclaration *td = getFuncTemplateDecl(f); |
| if (td) |
| { |
| if (td->overroot) // if not start of overloaded list of TemplateDeclaration's |
| td = td->overroot; // then get the start |
| e = new TemplateExp(exp->loc, td, f); |
| e = semantic(e, sc); |
| result = e; |
| return; |
| } |
| } |
| // Haven't done overload resolution yet, so pass 1 |
| e = resolve(exp->loc, sc, s, true); |
| } |
| result = e; |
| return; |
| } |
| |
| if (hasThis(sc)) |
| { |
| AggregateDeclaration *ad = sc->getStructClassScope(); |
| if (ad && ad->aliasthis) |
| { |
| Expression *e; |
| e = new IdentifierExp(exp->loc, Id::This); |
| e = new DotIdExp(exp->loc, e, ad->aliasthis->ident); |
| e = new DotIdExp(exp->loc, e, exp->ident); |
| e = trySemantic(e, sc); |
| if (e) |
| { |
| result = e; |
| return; |
| } |
| } |
| } |
| |
| if (exp->ident == Id::ctfe) |
| { |
| if (sc->flags & SCOPEctfe) |
| { |
| exp->error("variable __ctfe cannot be read at compile time"); |
| return setError(); |
| } |
| |
| // Create the magic __ctfe bool variable |
| VarDeclaration *vd = new VarDeclaration(exp->loc, Type::tbool, Id::ctfe, NULL); |
| vd->storage_class |= STCtemp; |
| Expression *e = new VarExp(exp->loc, vd); |
| e = semantic(e, sc); |
| result = e; |
| return; |
| } |
| |
| // If we've reached this point and are inside a with() scope then we may |
| // try one last attempt by checking whether the 'wthis' object supports |
| // dynamic dispatching via opDispatch. |
| // This is done by rewriting this expression as wthis.ident. |
| for (Scope *sc2 = sc; sc2; sc2 = sc2->enclosing) |
| { |
| if (!sc2->scopesym) |
| continue; |
| |
| if (WithScopeSymbol *ss = sc2->scopesym->isWithScopeSymbol()) |
| { |
| if (ss->withstate->wthis) |
| { |
| Expression *e; |
| e = new VarExp(exp->loc, ss->withstate->wthis); |
| e = new DotIdExp(exp->loc, e, exp->ident); |
| e = trySemantic(e, sc); |
| if (e) |
| { |
| result = e; |
| return; |
| } |
| } |
| break; |
| } |
| } |
| |
| /* Look for what user might have meant |
| */ |
| if (const char *n = importHint(exp->ident->toChars())) |
| exp->error("`%s` is not defined, perhaps `import %s;` is needed?", exp->ident->toChars(), n); |
| else if (Dsymbol *s2 = sc->search_correct(exp->ident)) |
| exp->error("undefined identifier `%s`, did you mean %s `%s`?", exp->ident->toChars(), s2->kind(), s2->toChars()); |
| else if (const char *p = Scope::search_correct_C(exp->ident)) |
| exp->error("undefined identifier `%s`, did you mean `%s`?", exp->ident->toChars(), p); |
| else |
| exp->error("undefined identifier `%s`", exp->ident->toChars()); |
| return setError(); |
| } |
| |
| void visit(DsymbolExp *e) |
| { |
| result = resolve(e->loc, sc, e->s, e->hasOverloads); |
| } |
| |
| void visit(ThisExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| FuncDeclaration *fd = hasThis(sc); // fd is the uplevel function with the 'this' variable |
| |
| /* Special case for typeof(this) and typeof(super) since both |
| * should work even if they are not inside a non-static member function |
| */ |
| if (!fd && sc->intypeof == 1) |
| { |
| // Find enclosing struct or class |
| for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent) |
| { |
| if (!s) |
| { |
| e->error("%s is not in a class or struct scope", e->toChars()); |
| goto Lerr; |
| } |
| ClassDeclaration *cd = s->isClassDeclaration(); |
| if (cd) |
| { |
| e->type = cd->type; |
| result = e; |
| return; |
| } |
| StructDeclaration *sd = s->isStructDeclaration(); |
| if (sd) |
| { |
| e->type = sd->type; |
| result = e; |
| return; |
| } |
| } |
| } |
| if (!fd) |
| goto Lerr; |
| |
| assert(fd->vthis); |
| e->var = fd->vthis; |
| assert(e->var->parent); |
| e->type = e->var->type; |
| if (e->var->checkNestedReference(sc, e->loc)) |
| return setError(); |
| if (!sc->intypeof) |
| sc->callSuper |= CSXthis; |
| result = e; |
| return; |
| |
| Lerr: |
| e->error("'this' is only defined in non-static member functions, not %s", sc->parent->toChars()); |
| return setError(); |
| } |
| |
| void visit(SuperExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| FuncDeclaration *fd = hasThis(sc); |
| ClassDeclaration *cd; |
| Dsymbol *s; |
| |
| /* Special case for typeof(this) and typeof(super) since both |
| * should work even if they are not inside a non-static member function |
| */ |
| if (!fd && sc->intypeof == 1) |
| { |
| // Find enclosing class |
| for (s = sc->getStructClassScope(); 1; s = s->parent) |
| { |
| if (!s) |
| { |
| e->error("%s is not in a class scope", e->toChars()); |
| goto Lerr; |
| } |
| cd = s->isClassDeclaration(); |
| if (cd) |
| { |
| cd = cd->baseClass; |
| if (!cd) |
| { |
| e->error("class %s has no 'super'", s->toChars()); |
| goto Lerr; |
| } |
| e->type = cd->type; |
| result = e; |
| return; |
| } |
| } |
| } |
| if (!fd) |
| goto Lerr; |
| |
| e->var = fd->vthis; |
| assert(e->var && e->var->parent); |
| |
| s = fd->toParent(); |
| while (s && s->isTemplateInstance()) |
| s = s->toParent(); |
| if (s->isTemplateDeclaration()) // allow inside template constraint |
| s = s->toParent(); |
| assert(s); |
| cd = s->isClassDeclaration(); |
| //printf("parent is %s %s\n", fd->toParent()->kind(), fd->toParent()->toChars()); |
| if (!cd) |
| goto Lerr; |
| if (!cd->baseClass) |
| { |
| e->error("no base class for %s", cd->toChars()); |
| e->type = e->var->type; |
| } |
| else |
| { |
| e->type = cd->baseClass->type; |
| e->type = e->type->castMod(e->var->type->mod); |
| } |
| |
| if (e->var->checkNestedReference(sc, e->loc)) |
| return setError(); |
| |
| if (!sc->intypeof) |
| sc->callSuper |= CSXsuper; |
| result = e; |
| return; |
| |
| Lerr: |
| e->error("'super' is only allowed in non-static class member functions"); |
| return setError(); |
| } |
| |
| void visit(NullExp *e) |
| { |
| // NULL is the same as (void *)0 |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| e->type = Type::tnull; |
| result = e; |
| } |
| |
| void visit(StringExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| OutBuffer buffer; |
| size_t newlen = 0; |
| const char *p; |
| size_t u; |
| unsigned c; |
| |
| switch (e->postfix) |
| { |
| case 'd': |
| for (u = 0; u < e->len;) |
| { |
| p = utf_decodeChar((utf8_t *)e->string, e->len, &u, &c); |
| if (p) |
| { |
| e->error("%s", p); |
| return setError(); |
| } |
| else |
| { |
| buffer.write4(c); |
| newlen++; |
| } |
| } |
| buffer.write4(0); |
| e->string = buffer.extractData(); |
| e->len = newlen; |
| e->sz = 4; |
| e->type = new TypeDArray(Type::tdchar->immutableOf()); |
| e->committed = 1; |
| break; |
| |
| case 'w': |
| for (u = 0; u < e->len;) |
| { |
| p = utf_decodeChar((utf8_t *)e->string, e->len, &u, &c); |
| if (p) |
| { |
| e->error("%s", p); |
| return setError(); |
| } |
| else |
| { |
| buffer.writeUTF16(c); |
| newlen++; |
| if (c >= 0x10000) |
| newlen++; |
| } |
| } |
| buffer.writeUTF16(0); |
| e->string = buffer.extractData(); |
| e->len = newlen; |
| e->sz = 2; |
| e->type = new TypeDArray(Type::twchar->immutableOf()); |
| e->committed = 1; |
| break; |
| |
| case 'c': |
| e->committed = 1; |
| /* fall through */ |
| |
| default: |
| e->type = new TypeDArray(Type::tchar->immutableOf()); |
| break; |
| } |
| e->type = e->type->semantic(e->loc, sc); |
| //e->type = e->type->immutableOf(); |
| //printf("type = %s\n", e->type->toChars()); |
| |
| result = e; |
| } |
| |
| void visit(ArrayLiteralExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| /* Perhaps an empty array literal [ ] should be rewritten as null? |
| */ |
| |
| if (e->basis) |
| e->basis = semantic(e->basis, sc); |
| if (arrayExpressionSemantic(e->elements, sc) || (e->basis && e->basis->op == TOKerror)) |
| return setError(); |
| expandTuples(e->elements); |
| |
| Type *t0; |
| if (e->basis) |
| e->elements->push(e->basis); |
| bool err = arrayExpressionToCommonType(sc, e->elements, &t0); |
| if (e->basis) |
| e->elements->pop(); |
| if (err) |
| return setError(); |
| |
| e->type = t0->arrayOf(); |
| e->type = e->type->semantic(e->loc, sc); |
| |
| /* Disallow array literals of type void being used. |
| */ |
| if (e->elements->dim > 0 && t0->ty == Tvoid) |
| { |
| e->error("%s of type %s has no value", e->toChars(), e->type->toChars()); |
| return setError(); |
| } |
| |
| if (global.params.useTypeInfo && Type::dtypeinfo) |
| semanticTypeInfo(sc, e->type); |
| |
| result = e; |
| } |
| |
| void visit(AssocArrayLiteralExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| // Run semantic() on each element |
| bool err_keys = arrayExpressionSemantic(e->keys, sc); |
| bool err_vals = arrayExpressionSemantic(e->values, sc); |
| if (err_keys || err_vals) |
| return setError(); |
| expandTuples(e->keys); |
| expandTuples(e->values); |
| if (e->keys->dim != e->values->dim) |
| { |
| e->error("number of keys is %u, must match number of values %u", e->keys->dim, e->values->dim); |
| return setError(); |
| } |
| |
| Type *tkey = NULL; |
| Type *tvalue = NULL; |
| err_keys = arrayExpressionToCommonType(sc, e->keys, &tkey); |
| err_vals = arrayExpressionToCommonType(sc, e->values, &tvalue); |
| if (err_keys || err_vals) |
| return setError(); |
| |
| if (tkey == Type::terror || tvalue == Type::terror) |
| return setError(); |
| |
| e->type = new TypeAArray(tvalue, tkey); |
| e->type = e->type->semantic(e->loc, sc); |
| |
| semanticTypeInfo(sc, e->type); |
| |
| result = e; |
| } |
| |
| void visit(StructLiteralExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| e->sd->size(e->loc); |
| if (e->sd->sizeok != SIZEOKdone) |
| return setError(); |
| |
| if (arrayExpressionSemantic(e->elements, sc)) // run semantic() on each element |
| return setError(); |
| expandTuples(e->elements); |
| |
| /* Fit elements[] to the corresponding type of field[]. |
| */ |
| if (!e->sd->fit(e->loc, sc, e->elements, e->stype)) |
| return setError(); |
| |
| /* Fill out remainder of elements[] with default initializers for fields[] |
| */ |
| if (!e->sd->fill(e->loc, e->elements, false)) |
| { |
| /* An error in the initializer needs to be recorded as an error |
| * in the enclosing function or template, since the initializer |
| * will be part of the stuct declaration. |
| */ |
| global.increaseErrorCount(); |
| return setError(); |
| } |
| |
| if (checkFrameAccess(e->loc, sc, e->sd, e->elements->dim)) |
| return setError(); |
| |
| e->type = e->stype ? e->stype : e->sd->type; |
| result = e; |
| } |
| |
| void visit(TypeExp *exp) |
| { |
| if (exp->type->ty == Terror) |
| return setError(); |
| |
| //printf("TypeExp::semantic(%s)\n", exp->type->toChars()); |
| Expression *e; |
| Type *t; |
| Dsymbol *s; |
| |
| exp->type->resolve(exp->loc, sc, &e, &t, &s, true); |
| if (e) |
| { |
| //printf("e = %s %s\n", Token::toChars(e->op), e->toChars()); |
| e = semantic(e, sc); |
| } |
| else if (t) |
| { |
| //printf("t = %d %s\n", t->ty, t->toChars()); |
| exp->type = t->semantic(exp->loc, sc); |
| e = exp; |
| } |
| else if (s) |
| { |
| //printf("s = %s %s\n", s->kind(), s->toChars()); |
| e = resolve(exp->loc, sc, s, true); |
| } |
| else |
| assert(0); |
| |
| if (global.params.vcomplex) |
| exp->type->checkComplexTransition(exp->loc); |
| |
| result = e; |
| } |
| |
| void visit(ScopeExp *exp) |
| { |
| if (exp->type) |
| { |
| result = exp; |
| return; |
| } |
| |
| ScopeDsymbol *sds2 = exp->sds; |
| TemplateInstance *ti = sds2->isTemplateInstance(); |
| while (ti) |
| { |
| WithScopeSymbol *withsym; |
| if (!ti->findTempDecl(sc, &withsym) || |
| !ti->semanticTiargs(sc)) |
| return setError(); |
| if (withsym && withsym->withstate->wthis) |
| { |
| Expression *e = new VarExp(exp->loc, withsym->withstate->wthis); |
| e = new DotTemplateInstanceExp(exp->loc, e, ti); |
| result = semantic(e, sc); |
| return; |
| } |
| if (ti->needsTypeInference(sc)) |
| { |
| if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration()) |
| { |
| Dsymbol *p = td->toParent2(); |
| FuncDeclaration *fdthis = hasThis(sc); |
| AggregateDeclaration *ad = p ? p->isAggregateDeclaration() : NULL; |
| if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad && |
| (td->_scope->stc & STCstatic) == 0) |
| { |
| Expression *e = new DotTemplateInstanceExp(exp->loc, new ThisExp(exp->loc), ti->name, ti->tiargs); |
| result = semantic(e, sc); |
| return; |
| } |
| } |
| else if (OverloadSet *os = ti->tempdecl->isOverloadSet()) |
| { |
| FuncDeclaration *fdthis = hasThis(sc); |
| AggregateDeclaration *ad = os->parent->isAggregateDeclaration(); |
| if (fdthis && ad && isAggregate(fdthis->vthis->type) == ad) |
| { |
| Expression *e = new DotTemplateInstanceExp(exp->loc, new ThisExp(exp->loc), ti->name, ti->tiargs); |
| result = semantic(e, sc); |
| return; |
| } |
| } |
| // ti is an instance which requires IFTI. |
| exp->sds = ti; |
| exp->type = Type::tvoid; |
| result = exp; |
| return; |
| } |
| ti->semantic(sc); |
| if (!ti->inst || ti->errors) |
| return setError(); |
| |
| Dsymbol *s = ti->toAlias(); |
| if (s == ti) |
| { |
| exp->sds = ti; |
| exp->type = Type::tvoid; |
| result = exp; |
| return; |
| } |
| sds2 = s->isScopeDsymbol(); |
| if (sds2) |
| { |
| ti = sds2->isTemplateInstance(); |
| //printf("+ sds2 = %s, '%s'\n", sds2->kind(), sds2->toChars()); |
| continue; |
| } |
| |
| if (VarDeclaration *v = s->isVarDeclaration()) |
| { |
| if (!v->type) |
| { |
| exp->error("forward reference of %s %s", v->kind(), v->toChars()); |
| return setError(); |
| } |
| if ((v->storage_class & STCmanifest) && v->_init) |
| { |
| /* When an instance that will be converted to a constant exists, |
| * the instance representation "foo!tiargs" is treated like a |
| * variable name, and its recursive appearance check (note that |
| * it's equivalent with a recursive instantiation of foo) is done |
| * separately from the circular initialization check for the |
| * eponymous enum variable declaration. |
| * |
| * template foo(T) { |
| * enum bool foo = foo; // recursive definition check (v.inuse) |
| * } |
| * template bar(T) { |
| * enum bool bar = bar!T; // recursive instantiation check (ti.inuse) |
| * } |
| */ |
| if (ti->inuse) |
| { |
| exp->error("recursive expansion of %s '%s'", ti->kind(), ti->toPrettyChars()); |
| return setError(); |
| } |
| |
| Expression *e = v->expandInitializer(exp->loc); |
| ti->inuse++; |
| e = semantic(e, sc); |
| ti->inuse--; |
| result = e; |
| return; |
| } |
| } |
| |
| //printf("s = %s, '%s'\n", s->kind(), s->toChars()); |
| Expression *e = resolve(exp->loc, sc, s, true); |
| //printf("-1ScopeExp::semantic()\n"); |
| result = e; |
| return; |
| } |
| |
| //printf("sds2 = %s, '%s'\n", sds2->kind(), sds2->toChars()); |
| //printf("\tparent = '%s'\n", sds2->parent->toChars()); |
| sds2->semantic(sc); |
| |
| if (Type *t = sds2->getType()) // (Aggregate|Enum)Declaration |
| { |
| Expression *ex = new TypeExp(exp->loc, t); |
| result = semantic(ex, sc); |
| return; |
| } |
| |
| if (TemplateDeclaration *td = sds2->isTemplateDeclaration()) |
| { |
| result = semantic(new TemplateExp(exp->loc, td), sc); |
| return; |
| } |
| |
| exp->sds = sds2; |
| exp->type = Type::tvoid; |
| //printf("-2ScopeExp::semantic() %s\n", exp->toChars()); |
| result = exp; |
| } |
| |
| void visit(NewExp *exp) |
| { |
| if (exp->type) // if semantic() already run |
| { |
| result = exp; |
| return; |
| } |
| |
| // Bugzilla 11581: With the syntax `new T[edim]` or `thisexp.new T[edim]`, |
| // T should be analyzed first and edim should go into arguments iff it's |
| // not a tuple. |
| Expression *edim = NULL; |
| if (!exp->arguments && exp->newtype->ty == Tsarray) |
| { |
| edim = ((TypeSArray *)exp->newtype)->dim; |
| exp->newtype = ((TypeNext *)exp->newtype)->next; |
| } |
| |
| ClassDeclaration *cdthis = NULL; |
| if (exp->thisexp) |
| { |
| exp->thisexp = semantic(exp->thisexp, sc); |
| if (exp->thisexp->op == TOKerror) |
| return setError(); |
| cdthis = exp->thisexp->type->isClassHandle(); |
| if (!cdthis) |
| { |
| exp->error("'this' for nested class must be a class type, not %s", exp->thisexp->type->toChars()); |
| return setError(); |
| } |
| |
| sc = sc->push(cdthis); |
| exp->type = exp->newtype->semantic(exp->loc, sc); |
| sc = sc->pop(); |
| } |
| else |
| { |
| exp->type = exp->newtype->semantic(exp->loc, sc); |
| } |
| if (exp->type->ty == Terror) |
| return setError(); |
| |
| if (edim) |
| { |
| if (exp->type->toBasetype()->ty == Ttuple) |
| { |
| // --> new T[edim] |
| exp->type = new TypeSArray(exp->type, edim); |
| exp->type = exp->type->semantic(exp->loc, sc); |
| if (exp->type->ty == Terror) |
| return setError(); |
| } |
| else |
| { |
| // --> new T[](edim) |
| exp->arguments = new Expressions(); |
| exp->arguments->push(edim); |
| exp->type = exp->type->arrayOf(); |
| } |
| } |
| |
| exp->newtype = exp->type; // in case type gets cast to something else |
| Type *tb = exp->type->toBasetype(); |
| //printf("tb: %s, deco = %s\n", tb->toChars(), tb->deco); |
| |
| if (arrayExpressionSemantic(exp->newargs, sc) || |
| preFunctionParameters(sc, exp->newargs)) |
| { |
| return setError(); |
| } |
| if (arrayExpressionSemantic(exp->arguments, sc) || |
| preFunctionParameters(sc, exp->arguments)) |
| { |
| return setError(); |
| } |
| |
| if (exp->thisexp && tb->ty != Tclass) |
| { |
| exp->error("e.new is only for allocating nested classes, not %s", tb->toChars()); |
| return setError(); |
| } |
| |
| size_t nargs = exp->arguments ? exp->arguments->dim : 0; |
| Expression *newprefix = NULL; |
| |
| if (tb->ty == Tclass) |
| { |
| ClassDeclaration *cd = ((TypeClass *)tb)->sym; |
| cd->size(exp->loc); |
| if (cd->sizeok != SIZEOKdone) |
| return setError(); |
| if (!cd->ctor) |
| cd->ctor = cd->searchCtor(); |
| if (cd->noDefaultCtor && !nargs && !cd->defaultCtor) |
| { |
| exp->error("default construction is disabled for type %s", cd->type->toChars()); |
| return setError(); |
| } |
| |
| if (cd->isInterfaceDeclaration()) |
| { |
| exp->error("cannot create instance of interface %s", cd->toChars()); |
| return setError(); |
| } |
| if (cd->isAbstract()) |
| { |
| exp->error("cannot create instance of abstract class %s", cd->toChars()); |
| for (size_t i = 0; i < cd->vtbl.dim; i++) |
| { |
| FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration(); |
| if (fd && fd->isAbstract()) |
| errorSupplemental(exp->loc, "function '%s' is not implemented", fd->toFullSignature()); |
| } |
| return setError(); |
| } |
| // checkDeprecated() is already done in newtype->semantic(). |
| |
| if (cd->isNested()) |
| { |
| /* We need a 'this' pointer for the nested class. |
| * Ensure we have the right one. |
| */ |
| Dsymbol *s = cd->toParent2(); |
| //printf("cd isNested, parent = %s '%s'\n", s->kind(), s->toPrettyChars()); |
| if (ClassDeclaration *cdn = s->isClassDeclaration()) |
| { |
| if (!cdthis) |
| { |
| // Supply an implicit 'this' and try again |
| exp->thisexp = new ThisExp(exp->loc); |
| for (Dsymbol *sp = sc->parent; 1; sp = sp->parent) |
| { |
| if (!sp) |
| { |
| exp->error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars()); |
| return setError(); |
| } |
| ClassDeclaration *cdp = sp->isClassDeclaration(); |
| if (!cdp) |
| continue; |
| if (cdp == cdn || cdn->isBaseOf(cdp, NULL)) |
| break; |
| // Add a '.outer' and try again |
| exp->thisexp = new DotIdExp(exp->loc, exp->thisexp, Id::outer); |
| } |
| exp->thisexp = semantic(exp->thisexp, sc); |
| if (exp->thisexp->op == TOKerror) |
| return setError(); |
| cdthis = exp->thisexp->type->isClassHandle(); |
| } |
| if (cdthis != cdn && !cdn->isBaseOf(cdthis, NULL)) |
| { |
| //printf("cdthis = %s\n", cdthis->toChars()); |
| exp->error("'this' for nested class must be of type %s, not %s", |
| cdn->toChars(), exp->thisexp->type->toChars()); |
| return setError(); |
| } |
| if (!MODimplicitConv(exp->thisexp->type->mod, exp->newtype->mod)) |
| { |
| exp->error("nested type %s should have the same or weaker constancy as enclosing type %s", |
| exp->newtype->toChars(), exp->thisexp->type->toChars()); |
| return setError(); |
| } |
| } |
| else if (exp->thisexp) |
| { |
| exp->error("e.new is only for allocating nested classes"); |
| return setError(); |
| } |
| else if (FuncDeclaration *fdn = s->isFuncDeclaration()) |
| { |
| // make sure the parent context fdn of cd is reachable from sc |
| if (checkNestedRef(sc->parent, fdn)) |
| { |
| exp->error("outer function context of %s is needed to 'new' nested class %s", |
| fdn->toPrettyChars(), cd->toPrettyChars()); |
| return setError(); |
| } |
| } |
| else |
| assert(0); |
| } |
| else if (exp->thisexp) |
| { |
| exp->error("e.new is only for allocating nested classes"); |
| return setError(); |
| } |
| |
| if (cd->aggNew) |
| { |
| // Prepend the size argument to newargs[] |
| Expression *e = new IntegerExp(exp->loc, cd->size(exp->loc), Type::tsize_t); |
| if (!exp->newargs) |
| exp->newargs = new Expressions(); |
| exp->newargs->shift(e); |
| |
| FuncDeclaration *f = resolveFuncCall(exp->loc, sc, cd->aggNew, NULL, tb, exp->newargs); |
| if (!f || f->errors) |
| return setError(); |
| exp->checkDeprecated(sc, f); |
| exp->checkPurity(sc, f); |
| exp->checkSafety(sc, f); |
| exp->checkNogc(sc, f); |
| checkAccess(cd, exp->loc, sc, f); |
| |
| TypeFunction *tf = (TypeFunction *)f->type; |
| Type *rettype; |
| if (functionParameters(exp->loc, sc, tf, NULL, exp->newargs, f, &rettype, &newprefix)) |
| return setError(); |
| |
| exp->allocator = f->isNewDeclaration(); |
| assert(exp->allocator); |
| } |
| else |
| { |
| if (exp->newargs && exp->newargs->dim) |
| { |
| exp->error("no allocator for %s", cd->toChars()); |
| return setError(); |
| } |
| } |
| |
| if (cd->ctor) |
| { |
| FuncDeclaration *f = resolveFuncCall(exp->loc, sc, cd->ctor, NULL, tb, exp->arguments, 0); |
| if (!f || f->errors) |
| return setError(); |
| exp->checkDeprecated(sc, f); |
| exp->checkPurity(sc, f); |
| exp->checkSafety(sc, f); |
| exp->checkNogc(sc, f); |
| checkAccess(cd, exp->loc, sc, f); |
| |
| TypeFunction *tf = (TypeFunction *)f->type; |
| if (!exp->arguments) |
| exp->arguments = new Expressions(); |
| if (functionParameters(exp->loc, sc, tf, exp->type, exp->arguments, f, &exp->type, &exp->argprefix)) |
| return setError(); |
| |
| exp->member = f->isCtorDeclaration(); |
| assert(exp->member); |
| } |
| else |
| { |
| if (nargs) |
| { |
| exp->error("no constructor for %s", cd->toChars()); |
| return setError(); |
| } |
| } |
| } |
| else if (tb->ty == Tstruct) |
| { |
| StructDeclaration *sd = ((TypeStruct *)tb)->sym; |
| sd->size(exp->loc); |
| if (sd->sizeok != SIZEOKdone) |
| return setError(); |
| if (!sd->ctor) |
| sd->ctor = sd->searchCtor(); |
| if (sd->noDefaultCtor && !nargs) |
| { |
| exp->error("default construction is disabled for type %s", sd->type->toChars()); |
| return setError(); |
| } |
| // checkDeprecated() is already done in newtype->semantic(). |
| |
| if (sd->aggNew) |
| { |
| // Prepend the uint size argument to newargs[] |
| Expression *e = new IntegerExp(exp->loc, sd->size(exp->loc), Type::tsize_t); |
| if (!exp->newargs) |
| exp->newargs = new Expressions(); |
| exp->newargs->shift(e); |
| |
| FuncDeclaration *f = resolveFuncCall(exp->loc, sc, sd->aggNew, NULL, tb, exp->newargs); |
| if (!f || f->errors) |
| return setError(); |
| exp->checkDeprecated(sc, f); |
| exp->checkPurity(sc, f); |
| exp->checkSafety(sc, f); |
| exp->checkNogc(sc, f); |
| checkAccess(sd, exp->loc, sc, f); |
| |
| TypeFunction *tf = (TypeFunction *)f->type; |
| Type *rettype; |
| if (functionParameters(exp->loc, sc, tf, NULL, exp->newargs, f, &rettype, &newprefix)) |
| return setError(); |
| |
| exp->allocator = f->isNewDeclaration(); |
| assert(exp->allocator); |
| } |
| else |
| { |
| if (exp->newargs && exp->newargs->dim) |
| { |
| exp->error("no allocator for %s", sd->toChars()); |
| return setError(); |
| } |
| } |
| |
| if (sd->ctor && nargs) |
| { |
| FuncDeclaration *f = resolveFuncCall(exp->loc, sc, sd->ctor, NULL, tb, exp->arguments, 0); |
| if (!f || f->errors) |
| return setError(); |
| exp->checkDeprecated(sc, f); |
| exp->checkPurity(sc, f); |
| exp->checkSafety(sc, f); |
| exp->checkNogc(sc, f); |
| checkAccess(sd, exp->loc, sc, f); |
| |
| TypeFunction *tf = (TypeFunction *)f->type; |
| if (!exp->arguments) |
| exp->arguments = new Expressions(); |
| if (functionParameters(exp->loc, sc, tf, exp->type, exp->arguments, f, &exp->type, &exp->argprefix)) |
| return setError(); |
| |
| exp->member = f->isCtorDeclaration(); |
| assert(exp->member); |
| |
| if (checkFrameAccess(exp->loc, sc, sd, sd->fields.dim)) |
| return setError(); |
| } |
| else |
| { |
| if (!exp->arguments) |
| exp->arguments = new Expressions(); |
| |
| if (!sd->fit(exp->loc, sc, exp->arguments, tb)) |
| return setError(); |
| if (!sd->fill(exp->loc, exp->arguments, false)) |
| return setError(); |
| if (checkFrameAccess(exp->loc, sc, sd, exp->arguments ? exp->arguments->dim : 0)) |
| return setError(); |
| } |
| |
| exp->type = exp->type->pointerTo(); |
| } |
| else if (tb->ty == Tarray && nargs) |
| { |
| Type *tn = tb->nextOf()->baseElemOf(); |
| Dsymbol *s = tn->toDsymbol(sc); |
| AggregateDeclaration *ad = s ? s->isAggregateDeclaration() : NULL; |
| if (ad && ad->noDefaultCtor) |
| { |
| exp->error("default construction is disabled for type %s", tb->nextOf()->toChars()); |
| return setError(); |
| } |
| for (size_t i = 0; i < nargs; i++) |
| { |
| if (tb->ty != Tarray) |
| { |
| exp->error("too many arguments for array"); |
| return setError(); |
| } |
| |
| Expression *arg = (*exp->arguments)[i]; |
| arg = resolveProperties(sc, arg); |
| arg = arg->implicitCastTo(sc, Type::tsize_t); |
| arg = arg->optimize(WANTvalue); |
| if (arg->op == TOKint64 && (sinteger_t)arg->toInteger() < 0) |
| { |
| exp->error("negative array index %s", arg->toChars()); |
| return setError(); |
| } |
| (*exp->arguments)[i] = arg; |
| tb = ((TypeDArray *)tb)->next->toBasetype(); |
| } |
| } |
| else if (tb->isscalar()) |
| { |
| if (!nargs) |
| { |
| } |
| else if (nargs == 1) |
| { |
| Expression *e = (*exp->arguments)[0]; |
| e = e->implicitCastTo(sc, tb); |
| (*exp->arguments)[0] = e; |
| } |
| else |
| { |
| exp->error("more than one argument for construction of %s", exp->type->toChars()); |
| return setError(); |
| } |
| |
| exp->type = exp->type->pointerTo(); |
| } |
| else |
| { |
| exp->error("new can only create structs, dynamic arrays or class objects, not %s's", exp->type->toChars()); |
| return setError(); |
| } |
| |
| //printf("NewExp: '%s'\n", toChars()); |
| //printf("NewExp:type '%s'\n", exp->type->toChars()); |
| semanticTypeInfo(sc, exp->type); |
| |
| if (newprefix) |
| { |
| result = Expression::combine(newprefix, exp); |
| return; |
| } |
| result = exp; |
| } |
| |
| void visit(NewAnonClassExp *e) |
| { |
| Expression *d = new DeclarationExp(e->loc, e->cd); |
| sc = sc->push(); // just create new scope |
| sc->flags &= ~SCOPEctfe; // temporary stop CTFE |
| d = semantic(d, sc); |
| sc = sc->pop(); |
| |
| if (!e->cd->errors && sc->intypeof && !sc->parent->inNonRoot()) |
| { |
| ScopeDsymbol *sds = sc->tinst ? (ScopeDsymbol *)sc->tinst : sc->_module; |
| sds->members->push(e->cd); |
| } |
| |
| Expression *n = new NewExp(e->loc, e->thisexp, e->newargs, e->cd->type, e->arguments); |
| |
| Expression *c = new CommaExp(e->loc, d, n); |
| result = semantic(c, sc); |
| } |
| |
| void visit(SymOffExp *e) |
| { |
| //var->semantic(sc); |
| if (!e->type) |
| e->type = e->var->type->pointerTo(); |
| if (VarDeclaration *v = e->var->isVarDeclaration()) |
| { |
| if (v->checkNestedReference(sc, e->loc)) |
| return setError(); |
| } |
| else if (FuncDeclaration *f = e->var->isFuncDeclaration()) |
| { |
| if (f->checkNestedReference(sc, e->loc)) |
| return setError(); |
| } |
| result = e; |
| } |
| |
| void visit(VarExp *e) |
| { |
| if (FuncDeclaration *fd = e->var->isFuncDeclaration()) |
| { |
| //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) |
| e->type = e->type->semantic(e->loc, sc); |
| |
| /* 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 (VarDeclaration *vd = e->var->isVarDeclaration()) |
| { |
| 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 (FuncDeclaration *fd = e->var->isFuncDeclaration()) |
| { |
| // TODO: If fd isn't yet resolved its overload, the checkNestedReference |
| // call would cause incorrect validation. |
| // Maybe here should be moved in CallExp, or AddrExp for functions. |
| if (fd->checkNestedReference(sc, e->loc)) |
| return setError(); |
| } |
| else if (e->var->isOverDeclaration()) |
| { |
| e->type = Type::tvoid; // ambiguous type? |
| } |
| |
| result = e; |
| } |
| |
| void visit(TupleExp *exp) |
| { |
| if (exp->type) |
| { |
| result = exp; |
| return; |
| } |
| |
| if (exp->e0) |
| exp->e0 = semantic(exp->e0, sc); |
| |
| // Run semantic() on each argument |
| bool err = false; |
| for (size_t i = 0; i < exp->exps->dim; i++) |
| { |
| Expression *e = (*exp->exps)[i]; |
| e = semantic(e, sc); |
| if (!e->type) |
| { |
| exp->error("%s has no value", e->toChars()); |
| err = true; |
| } |
| else if (e->op == TOKerror) |
| err = true; |
| else |
| (*exp->exps)[i] = e; |
| } |
| if (err) |
| return setError(); |
| |
| expandTuples(exp->exps); |
| exp->type = new TypeTuple(exp->exps); |
| exp->type = exp->type->semantic(exp->loc, sc); |
| //printf("-TupleExp::semantic(%s)\n", exp->toChars()); |
| result = exp; |
| } |
| |
| void visit(FuncExp *exp) |
| { |
| Expression *e = exp; |
| |
| sc = sc->push(); // just create new scope |
| sc->flags &= ~SCOPEctfe; // temporary stop CTFE |
| sc->protection = Prot(PROTpublic); // Bugzilla 12506 |
| |
| if (!exp->type || exp->type == Type::tvoid) |
| { |
| /* fd->treq might be incomplete type, |
| * so should not semantic it. |
| * void foo(T)(T delegate(int) dg){} |
| * foo(a=>a); // in IFTI, treq == T delegate(int) |
| */ |
| //if (exp->fd->treq) |
| // exp->fd->treq = exp->fd->treq->semantic(exp->loc, sc); |
| |
| exp->genIdent(sc); |
| |
| // Set target of return type inference |
| if (exp->fd->treq && !exp->fd->type->nextOf()) |
| { |
| TypeFunction *tfv = NULL; |
| if (exp->fd->treq->ty == Tdelegate || |
| (exp->fd->treq->ty == Tpointer && exp->fd->treq->nextOf()->ty == Tfunction)) |
| tfv = (TypeFunction *)exp->fd->treq->nextOf(); |
| if (tfv) |
| { |
| TypeFunction *tfl = (TypeFunction *)exp->fd->type; |
| tfl->next = tfv->nextOf(); |
| } |
| } |
| |
| //printf("td = %p, treq = %p\n", exp->td, exp->fd->treq); |
| if (exp->td) |
| { |
| assert(exp->td->parameters && exp->td->parameters->dim); |
| exp->td->semantic(sc); |
| exp->type = Type::tvoid; // temporary type |
| |
| if (exp->fd->treq) // defer type determination |
| { |
| FuncExp *fe; |
| if (exp->matchType(exp->fd->treq, sc, &fe) > MATCHnomatch) |
| e = fe; |
| else |
| e = new ErrorExp(); |
| } |
| goto Ldone; |
| } |
| |
| unsigned olderrors = global.errors; |
| exp->fd->semantic(sc); |
| if (olderrors == global.errors) |
| { |
| exp->fd->semantic2(sc); |
| if (olderrors == global.errors) |
| exp->fd->semantic3(sc); |
| } |
| if (olderrors != global.errors) |
| { |
| if (exp->fd->type && exp->fd->type->ty == Tfunction && !exp->fd->type->nextOf()) |
| ((TypeFunction *)exp->fd->type)->next = Type::terror; |
| e = new ErrorExp(); |
| goto Ldone; |
| } |
| |
| // Type is a "delegate to" or "pointer to" the function literal |
| if ((exp->fd->isNested() && exp->fd->tok == TOKdelegate) || |
| (exp->tok == TOKreserved && exp->fd->treq && exp->fd->treq->ty == Tdelegate)) |
| { |
| exp->type = new TypeDelegate(exp->fd->type); |
| exp->type = exp->type->semantic(exp->loc, sc); |
| |
| exp->fd->tok = TOKdelegate; |
| } |
| else |
| { |
| exp->type = new TypePointer(exp->fd->type); |
| exp->type = exp->type->semantic(exp->loc, sc); |
| //exp->type = exp->fd->type->pointerTo(); |
| |
| /* A lambda expression deduced to function pointer might become |
| * to a delegate literal implicitly. |
| * |
| * auto foo(void function() fp) { return 1; } |
| * assert(foo({}) == 1); |
| * |
| * So, should keep fd->tok == TOKreserve if fd->treq == NULL. |
| */ |
| if (exp->fd->treq && exp->fd->treq->ty == Tpointer) |
| { |
| // change to non-nested |
| exp->fd->tok = TOKfunction; |
| exp->fd->vthis = NULL; |
| } |
| } |
| exp->fd->tookAddressOf++; |
| } |
| Ldone: |
| sc = sc->pop(); |
| result = e; |
| } |
| |
| // used from CallExp::semantic() |
| Expression *callExpSemantic(FuncExp *exp, Scope *sc, Expressions *arguments) |
| { |
| if ((!exp->type || exp->type == Type::tvoid) && exp->td && arguments && arguments->dim) |
| { |
| for (size_t k = 0; k < arguments->dim; k++) |
| { Expression *checkarg = (*arguments)[k]; |
| if (checkarg->op == TOKerror) |
| return checkarg; |
| } |
| |
| exp->genIdent(sc); |
| |
| assert(exp->td->parameters && exp->td->parameters->dim); |
| exp->td->semantic(sc); |
| |
| TypeFunction *tfl = (TypeFunction *)exp->fd->type; |
| size_t dim = Parameter::dim(tfl->parameters); |
| if (arguments->dim < dim) |
| { // Default arguments are always typed, so they don't need inference. |
| Parameter *p = Parameter::getNth(tfl->parameters, arguments->dim); |
| if (p->defaultArg) |
| dim = arguments->dim; |
| } |
| |
| if ((!tfl->varargs && arguments->dim == dim) || |
| ( tfl->varargs && arguments->dim >= dim)) |
| { |
| Objects *tiargs = new Objects(); |
| tiargs->reserve(exp->td->parameters->dim); |
| |
| for (size_t i = 0; i < exp->td->parameters->dim; i++) |
| { |
| TemplateParameter *tp = (*exp->td->parameters)[i]; |
| for (size_t u = 0; u < dim; u++) |
| { Parameter *p = Parameter::getNth(tfl->parameters, u); |
| if (p->type->ty == Tident && |
| ((TypeIdentifier *)p->type)->ident == tp->ident) |
| { Expression *e = (*arguments)[u]; |
| tiargs->push(e->type); |
| u = dim; // break inner loop |
| } |
| } |
| } |
| |
| TemplateInstance *ti = new TemplateInstance(exp->loc, exp->td, tiargs); |
| Expression *se = new ScopeExp(exp->loc, ti); |
| return semantic(se, sc); |
| } |
| exp->error("cannot infer function literal type"); |
| return new ErrorExp(); |
| } |
| return semantic(exp, sc); |
| } |
| |
| void visit(DeclarationExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| unsigned olderrors = global.errors; |
| |
| /* This is here to support extern(linkage) declaration, |
| * where the extern(linkage) winds up being an AttribDeclaration |
| * wrapper. |
| */ |
| Dsymbol *s = e->declaration; |
| |
| while (1) |
| { |
| AttribDeclaration *ad = s->isAttribDeclaration(); |
| if (ad) |
| { |
| if (ad->decl && ad->decl->dim == 1) |
| { |
| s = (*ad->decl)[0]; |
| continue; |
| } |
| } |
| break; |
| } |
| |
| VarDeclaration *v = s->isVarDeclaration(); |
| if (v) |
| { |
| // Do semantic() on initializer first, so: |
| // int a = a; |
| // will be illegal. |
| e->declaration->semantic(sc); |
| s->parent = sc->parent; |
| } |
| |
| //printf("inserting '%s' %p into sc = %p\n", s->toChars(), s, sc); |
| // Insert into both local scope and function scope. |
| // Must be unique in both. |
| if (s->ident) |
| { |
| if (!sc->insert(s)) |
| { |
| e->error("declaration %s is already defined", s->toPrettyChars()); |
| return setError(); |
| } |
| else if (sc->func) |
| { |
| // Bugzilla 11720 - include Dataseg variables |
| if ((s->isFuncDeclaration() || |
| s->isAggregateDeclaration() || |
| s->isEnumDeclaration() || |
| (v && v->isDataseg())) && |
| !sc->func->localsymtab->insert(s)) |
| { |
| e->error("declaration %s is already defined in another scope in %s", |
| s->toPrettyChars(), sc->func->toChars()); |
| return setError(); |
| } |
| else |
| { |
| // Disallow shadowing |
| for (Scope *scx = sc->enclosing; scx && scx->func == sc->func; scx = scx->enclosing) |
| { |
| Dsymbol *s2; |
| if (scx->scopesym && scx->scopesym->symtab && |
| (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL && |
| s != s2) |
| { |
| e->error("%s %s is shadowing %s %s", s->kind(), s->ident->toChars(), s2->kind(), s2->toPrettyChars()); |
| return setError(); |
| } |
| } |
| } |
| } |
| } |
| if (!s->isVarDeclaration()) |
| { |
| Scope *sc2 = sc; |
| if (sc2->stc & (STCpure | STCnothrow | STCnogc)) |
| sc2 = sc->push(); |
| sc2->stc &= ~(STCpure | STCnothrow | STCnogc); |
| e->declaration->semantic(sc2); |
| if (sc2 != sc) |
| sc2->pop(); |
| s->parent = sc->parent; |
| } |
| if (global.errors == olderrors) |
| { |
| e->declaration->semantic2(sc); |
| if (global.errors == olderrors) |
| { |
| e->declaration->semantic3(sc); |
| } |
| } |
| // todo: error in declaration should be propagated. |
| |
| e->type = Type::tvoid; |
| result = e; |
| } |
| |
| void visit(TypeidExp *exp) |
| { |
| Type *ta = isType(exp->obj); |
| Expression *ea = isExpression(exp->obj); |
| Dsymbol *sa = isDsymbol(exp->obj); |
| |
| //printf("ta %p ea %p sa %p\n", ta, ea, sa); |
| |
| if (ta) |
| { |
| ta->resolve(exp->loc, sc, &ea, &ta, &sa, true); |
| } |
| |
| if (ea) |
| { |
| if (Dsymbol *sym = getDsymbol(ea)) |
| ea = resolve(exp->loc, sc, sym, false); |
| else |
| ea = semantic(ea, sc); |
| ea = resolveProperties(sc, ea); |
| ta = ea->type; |
| if (ea->op == TOKtype) |
| ea = NULL; |
| } |
| |
| if (!ta) |
| { |
| //printf("ta %p ea %p sa %p\n", ta, ea, sa); |
| exp->error("no type for typeid(%s)", ea ? ea->toChars() : (sa ? sa->toChars() : "")); |
| return setError(); |
| } |
| |
| if (global.params.vcomplex) |
| ta->checkComplexTransition(exp->loc); |
| |
| Expression *e; |
| if (ea && ta->toBasetype()->ty == Tclass) |
| { |
| if (!Type::typeinfoclass) |
| { |
| error(exp->loc, "`object.TypeInfo_Class` could not be found, but is implicitly used"); |
| e = new ErrorExp(); |
| } |
| else |
| { |
| /* Get the dynamic type, which is .classinfo |
| */ |
| ea = semantic(ea, sc); |
| e = new TypeidExp(ea->loc, ea); |
| e->type = Type::typeinfoclass->type; |
| } |
| } |
| else if (ta->ty == Terror) |
| { |
| e = new ErrorExp(); |
| } |
| else |
| { |
| // Handle this in the glue layer |
| e = new TypeidExp(exp->loc, ta); |
| e->type = getTypeInfoType(exp->loc, ta, sc); |
| |
| semanticTypeInfo(sc, ta); |
| |
| if (ea) |
| { |
| e = new CommaExp(exp->loc, ea, e); // execute ea |
| e = semantic(e, sc); |
| } |
| } |
| result = e; |
| } |
| |
| void visit(TraitsExp *e) |
| { |
| result = semanticTraits(e, sc); |
| } |
| |
| void visit(HaltExp *e) |
| { |
| e->type = Type::tvoid; |
| result = e; |
| } |
| |
| void visit(IsExp *e) |
| { |
| /* is(targ id tok tspec) |
| * is(targ id : tok2) |
| * is(targ id == tok2) |
| */ |
| |
| //printf("IsExp::semantic(%s)\n", toChars()); |
| if (e->id && !(sc->flags & SCOPEcondition)) |
| { |
| e->error("can only declare type aliases within static if conditionals or static asserts"); |
| return setError(); |
| } |
| |
| Type *tded = NULL; |
| Scope *sc2 = sc->copy(); // keep sc->flags |
| sc2->tinst = NULL; |
| sc2->minst = NULL; |
| sc2->flags |= SCOPEfullinst; |
| Type *t = e->targ->trySemantic(e->loc, sc2); |
| sc2->pop(); |
| if (!t) |
| goto Lno; // errors, so condition is false |
| e->targ = t; |
| if (e->tok2 != TOKreserved) |
| { |
| switch (e->tok2) |
| { |
| case TOKstruct: |
| if (e->targ->ty != Tstruct) |
| goto Lno; |
| if (((TypeStruct *)e->targ)->sym->isUnionDeclaration()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| |
| case TOKunion: |
| if (e->targ->ty != Tstruct) |
| goto Lno; |
| if (!((TypeStruct *)e->targ)->sym->isUnionDeclaration()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| |
| case TOKclass: |
| if (e->targ->ty != Tclass) |
| goto Lno; |
| if (((TypeClass *)e->targ)->sym->isInterfaceDeclaration()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| |
| case TOKinterface: |
| if (e->targ->ty != Tclass) |
| goto Lno; |
| if (!((TypeClass *)e->targ)->sym->isInterfaceDeclaration()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| case TOKconst: |
| if (!e->targ->isConst()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| |
| case TOKimmutable: |
| if (!e->targ->isImmutable()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| |
| case TOKshared: |
| if (!e->targ->isShared()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| |
| case TOKwild: |
| if (!e->targ->isWild()) |
| goto Lno; |
| tded = e->targ; |
| break; |
| |
| case TOKsuper: |
| // If class or interface, get the base class and interfaces |
| if (e->targ->ty != Tclass) |
| goto Lno; |
| else |
| { |
| ClassDeclaration *cd = ((TypeClass *)e->targ)->sym; |
| Parameters *args = new Parameters; |
| args->reserve(cd->baseclasses->dim); |
| if (cd->_scope && !cd->symtab) |
| cd->semantic(cd->_scope); |
| for (size_t i = 0; i < cd->baseclasses->dim; i++) |
| { |
| BaseClass *b = (*cd->baseclasses)[i]; |
| args->push(new Parameter(STCin, b->type, NULL, NULL)); |
| } |
| tded = new TypeTuple(args); |
| } |
| break; |
| |
| case TOKenum: |
| if (e->targ->ty != Tenum) |
| goto Lno; |
| if (e->id) |
| tded = ((TypeEnum *)e->targ)->sym->getMemtype(e->loc); |
| else |
| tded = e->targ; |
| if (tded->ty == Terror) |
| return setError(); |
| break; |
| |
| case TOKdelegate: |
| if (e->targ->ty != Tdelegate) |
| goto Lno; |
| tded = ((TypeDelegate *)e->targ)->next; // the underlying function type |
| break; |
| |
| case TOKfunction: |
| case TOKparameters: |
| { |
| if (e->targ->ty != Tfunction) |
| goto Lno; |
| tded = e->targ; |
| |
| /* Generate tuple from function parameter types. |
| */ |
| assert(tded->ty == Tfunction); |
| Parameters *params = ((TypeFunction *)tded)->parameters; |
| size_t dim = Parameter::dim(params); |
| Parameters *args = new Parameters; |
| args->reserve(dim); |
| for (size_t i = 0; i < dim; i++) |
| { |
| Parameter *arg = Parameter::getNth(params, i); |
| assert(arg && arg->type); |
| /* If one of the default arguments was an error, |
| don't return an invalid tuple |
| */ |
| if (e->tok2 == TOKparameters && arg->defaultArg && |
| arg->defaultArg->op == TOKerror) |
| return setError(); |
| args->push(new Parameter(arg->storageClass, arg->type, |
| (e->tok2 == TOKparameters) ? arg->ident : NULL, |
| (e->tok2 == TOKparameters) ? arg->defaultArg : NULL)); |
| } |
| tded = new TypeTuple(args); |
| break; |
| } |
| case TOKreturn: |
| /* Get the 'return type' for the function, |
| * delegate, or pointer to function. |
| */ |
| if (e->targ->ty == Tfunction) |
| tded = ((TypeFunction *)e->targ)->next; |
| else if (e->targ->ty == Tdelegate) |
| { |
| tded = ((TypeDelegate *)e->targ)->next; |
| tded = ((TypeFunction *)tded)->next; |
| } |
| else if (e->targ->ty == Tpointer && |
| ((TypePointer *)e->targ)->next->ty == Tfunction) |
| { |
| tded = ((TypePointer *)e->targ)->next; |
| tded = ((TypeFunction *)tded)->next; |
| } |
| else |
| goto Lno; |
| break; |
| |
| case TOKargTypes: |
| /* Generate a type tuple of the equivalent types used to determine if a |
| * function argument of this type can be passed in registers. |
| * The results of this are highly platform dependent, and intended |
| * primarly for use in implementing va_arg(). |
| */ |
| tded = toArgTypes(e->targ); |
| if (!tded) |
| goto Lno; // not valid for a parameter |
| break; |
| |
| case TOKvector: |
| if (e->targ->ty != Tvector) |
| goto Lno; |
| tded = ((TypeVector *)e->targ)->basetype; |
| break; |
| |
| default: |
| assert(0); |
| } |
| goto Lyes; |
| } |
| else if (e->tspec && !e->id && !(e->parameters && e->parameters->dim)) |
| { |
| /* Evaluate to true if targ matches tspec |
| * is(targ == tspec) |
| * is(targ : tspec) |
| */ |
| e->tspec = e->tspec->semantic(e->loc, sc); |
| //printf("targ = %s, %s\n", e->targ->toChars(), e->targ->deco); |
| //printf("tspec = %s, %s\n", e->tspec->toChars(), e->tspec->deco); |
| if (e->tok == TOKcolon) |
| { |
| if (e->targ->implicitConvTo(e->tspec)) |
| goto Lyes; |
| else |
| goto Lno; |
| } |
| else /* == */ |
| { |
| if (e->targ->equals(e->tspec)) |
| goto Lyes; |
| else |
| goto Lno; |
| } |
| } |
| else if (e->tspec) |
| { |
| /* Evaluate to true if targ matches tspec. |
| * If true, declare id as an alias for the specialized type. |
| * is(targ == tspec, tpl) |
| * is(targ : tspec, tpl) |
| * is(targ id == tspec) |
| * is(targ id : tspec) |
| * is(targ id == tspec, tpl) |
| * is(targ id : tspec, tpl) |
| */ |
| |
| Identifier *tid = e->id ? e->id : Identifier::generateId("__isexp_id"); |
| e->parameters->insert(0, new TemplateTypeParameter(e->loc, tid, NULL, NULL)); |
| |
| Objects dedtypes; |
| dedtypes.setDim(e->parameters->dim); |
| dedtypes.zero(); |
| |
| MATCH m = deduceType(e->targ, sc, e->tspec, e->parameters, &dedtypes); |
| //printf("targ: %s\n", e->targ->toChars()); |
| //printf("tspec: %s\n", e->tspec->toChars()); |
| if (m <= MATCHnomatch || |
| (m != MATCHexact && e->tok == TOKequal)) |
| { |
| goto Lno; |
| } |
| else |
| { |
| tded = (Type *)dedtypes[0]; |
| if (!tded) |
| tded = e->targ; |
| Objects tiargs; |
| tiargs.setDim(1); |
| tiargs[0] = e->targ; |
| |
| /* Declare trailing parameters |
| */ |
| for (size_t i = 1; i < e->parameters->dim; i++) |
| { |
| TemplateParameter *tp = (*e->parameters)[i]; |
| Declaration *s = NULL; |
| |
| m = tp->matchArg(e->loc, sc, &tiargs, i, e->parameters, &dedtypes, &s); |
| if (m <= MATCHnomatch) |
| goto Lno; |
| s->semantic(sc); |
| if (sc->sds) |
| s->addMember(sc, sc->sds); |
| else if (!sc->insert(s)) |
| e->error("declaration %s is already defined", s->toChars()); |
| |
| unSpeculative(sc, s); |
| } |
| goto Lyes; |
| } |
| } |
| else if (e->id) |
| { |
| /* Declare id as an alias for type targ. Evaluate to true |
| * is(targ id) |
| */ |
| tded = e->targ; |
| goto Lyes; |
| } |
| |
| Lyes: |
| if (e->id) |
| { |
| Dsymbol *s; |
| Tuple *tup = isTuple(tded); |
| if (tup) |
| s = new TupleDeclaration(e->loc, e->id, &(tup->objects)); |
| else |
| s = new AliasDeclaration(e->loc, e->id, tded); |
| s->semantic(sc); |
| /* The reason for the !tup is unclear. It fails Phobos unittests if it is not there. |
| * More investigation is needed. |
| */ |
| if (!tup && !sc->insert(s)) |
| e->error("declaration %s is already defined", s->toChars()); |
| if (sc->sds) |
| s->addMember(sc, sc->sds); |
| |
| unSpeculative(sc, s); |
| } |
| //printf("Lyes\n"); |
| result = new IntegerExp(e->loc, 1, Type::tbool); |
| return; |
| |
| Lno: |
| //printf("Lno\n"); |
| result = new IntegerExp(e->loc, 0, Type::tbool); |
| } |
| |
| void visit(BinAssignExp *exp) |
| { |
| if (exp->type) |
| { |
| result = exp; |
| return; |
| } |
| |
| Expression *e = exp->op_overload(sc); |
| if (e) |
| { |
| result = e; |
| return; |
| } |
| |
| if (exp->e1->checkReadModifyWrite(exp->op, exp->e2)) |
| return setError(); |
| |
| if (exp->e1->op == TOKarraylength) |
| { |
| // arr.length op= e2; |
| e = ArrayLengthExp::rewriteOpAssign(exp); |
| e = semantic(e, sc); |
| result = e; |
| return; |
| } |
| if (exp->e1->op == TOKslice || exp->e1->type->ty == Tarray || exp->e1->type->ty == Tsarray) |
| { |
| if (exp->e1->op == TOKslice) |
| ((SliceExp *)exp->e1)->arrayop = true; |
| |
| // T[] op= ... |
| if (exp->e2->implicitConvTo(exp->e1->type->nextOf())) |
| { |
| // T[] op= T |
| exp->e2 = exp->e2->castTo(sc, exp->e1->type->nextOf()); |
| } |
| else if (Expression *ex = typeCombine(exp, sc)) |
| { |
| result = ex; |
| return; |
| } |
| exp->type = exp->e1->type; |
| result = arrayOp(exp, sc); |
| return; |
| } |
| |
| exp->e1 = semantic(exp->e1, sc); |
| exp->e1 = exp->e1->optimize(WANTvalue); |
| exp->e1 = exp->e1->modifiableLvalue(sc, exp->e1); |
| exp->type = exp->e1->type; |
| if (exp->checkScalar()) |
| return setError(); |
| |
| int arith = (exp->op == TOKaddass || exp->op == TOKminass || exp->op == TOKmulass || |
| exp->op == TOKdivass || exp->op == TOKmodass || exp->op == TOKpowass); |
| int bitwise = (exp->op == TOKandass || exp->op == TOKorass || exp->op == TOKxorass); |
| int shift = (exp->op == TOKshlass || exp->op == TOKshrass || exp->op == TOKushrass); |
| |
| if (bitwise && exp->type->toBasetype()->ty == Tbool) |
| exp->e2 = exp->e2->implicitCastTo(sc, exp->type); |
| else if (exp->checkNoBool()) |
| return setError(); |
| |
| if ((exp->op == TOKaddass || exp->op == TOKminass) && |
| exp->e1->type->toBasetype()->ty == Tpointer && |
| exp->e2->type->toBasetype()->isintegral()) |
| { |
| result = scaleFactor(exp, sc); |
| return; |
| } |
| |
| if (Expression *ex = typeCombine(exp, sc)) |
| { |
| result = ex; |
| return; |
| } |
| |
| if (arith && exp->checkArithmeticBin()) |
| return setError(); |
| if ((bitwise || shift) && exp->checkIntegralBin()) |
| return setError(); |
| if (shift) |
| { |
| if (exp->e2->type->toBasetype()->ty != Tvector) |
| exp->e2 = exp->e2->castTo(sc, Type::tshiftcnt); |
| } |
| |
| if (!Target::isVectorOpSupported(exp->type->toBasetype(), exp->op, exp->e2->type->toBasetype())) |
| { |
| result = exp->incompatibleTypes(); |
| return; |
| } |
| |
| if (exp->e1->op == TOKerror || exp->e2->op == TOKerror) |
| return setError(); |
| |
| e = exp->checkOpAssignTypes(sc); |
| if (e->op == TOKerror) |
| { |
| result = e; |
| return; |
| } |
| |
| assert(e->op == TOKassign || e == exp); |
| result = ((BinExp *)e)->reorderSettingAAElem(sc); |
| } |
| |
| void visit(CompileExp *exp) |
| { |
| StringExp *se = semanticString(sc, exp->e1, "argument to mixin"); |
| if (!se) |
| return setError(); |
| se = se->toUTF8(sc); |
| unsigned errors = global.errors; |
| Parser p(exp->loc, sc->_module, (utf8_t *)se->string, se->len, 0); |
| p.nextToken(); |
| //printf("p.loc.linnum = %d\n", p.loc.linnum); |
| Expression *e = p.parseExpression(); |
| if (p.errors) |
| { |
| assert(global.errors != errors); // should have caught all these cases |
| return setError(); |
| } |
| if (p.token.value != TOKeof) |
| { |
| exp->error("incomplete mixin expression (%s)", se->toChars()); |
| return setError(); |
| } |
| result = semantic(e, sc); |
| } |
| |
| void visit(ImportExp *e) |
| { |
| StringExp *se = semanticString(sc, e->e1, "file name argument"); |
| if (!se) |
| return setError(); |
| se = se->toUTF8(sc); |
| |
| const char *name = (char *)se->string; |
| if (!global.params.fileImppath) |
| { |
| e->error("need -Jpath switch to import text file %s", name); |
| return setError(); |
| } |
| |
| /* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory |
| * ('Path Traversal') attacks. |
| * http://cwe.mitre.org/data/definitions/22.html |
| */ |
| |
| name = FileName::safeSearchPath(global.filePath, name); |
| if (!name) |
| { |
| e->error("file %s cannot be found or not in a path specified with -J", se->toChars()); |
| return setError(); |
| } |
| |
| if (global.params.verbose) |
| message("file %.*s\t(%s)", (int)se->len, (char *)se->string, name); |
| if (global.params.moduleDeps != NULL) |
| { |
| OutBuffer *ob = global.params.moduleDeps; |
| Module* imod = sc->instantiatingModule(); |
| |
| if (!global.params.moduleDepsFile) |
| ob->writestring("depsFile "); |
| ob->writestring(imod->toPrettyChars()); |
| ob->writestring(" ("); |
| escapePath(ob, imod->srcfile->toChars()); |
| ob->writestring(") : "); |
| if (global.params.moduleDepsFile) |
| ob->writestring("string : "); |
| ob->writestring((char *) se->string); |
| ob->writestring(" ("); |
| escapePath(ob, name); |
| ob->writestring(")"); |
| ob->writenl(); |
| } |
| |
| { |
| File f(name); |
| if (f.read()) |
| { |
| e->error("cannot read file %s", f.toChars()); |
| return setError(); |
| } |
| else |
| { |
| f.ref = 1; |
| se = new StringExp(e->loc, f.buffer, f.len); |
| } |
| } |
| result = semantic(se, sc); |
| } |
| |
| void visit(AssertExp *exp) |
| { |
| if (Expression *ex = unaSemantic(exp, sc)) |
| { |
| result = ex; |
| return; |
| } |
| exp->e1 = resolveProperties(sc, exp->e1); |
| // BUG: see if we can do compile time elimination of the Assert |
| exp->e1 = exp->e1->optimize(WANTvalue); |
| exp->e1 = exp->e1->toBoolean(sc); |
| if (exp->msg) |
| { |
| exp->msg = semantic(exp->msg, sc); |
| exp->msg = resolveProperties(sc, exp->msg); |
| exp->msg = exp->msg->implicitCastTo(sc, Type::tchar->constOf()->arrayOf()); |
| exp->msg = exp->msg->optimize(WANTvalue); |
| } |
| |
| if (exp->e1->op == TOKerror) |
| { |
| result = exp->e1; |
| return; |
| } |
| if (exp->msg && exp->msg->op == TOKerror) |
| { |
| result = exp->msg; |
| return; |
| } |
| |
| bool f1 = checkNonAssignmentArrayOp(exp->e1); |
| bool f2 = exp->msg && checkNonAssignmentArrayOp(exp->msg); |
| if (f1 || f2) |
| return setError(); |
| |
| if (exp->e1->isBool(false)) |
| { |
| FuncDeclaration *fd = sc->parent->isFuncDeclaration(); |
| if (fd) |
| fd->hasReturnExp |= 4; |
| sc->callSuper |= CSXhalt; |
| if (sc->fieldinit) |
| { |
| for (size_t i = 0; i < sc->fieldinit_dim; i++) |
| sc->fieldinit[i] |= CSXhalt; |
| } |
| |
| if (!global.params.useAssert) |
| { |
| Expression *e = new HaltExp(exp->loc); |
| e = semantic(e, sc); |
| result = e; |
| return; |
| } |
| } |
| exp->type = Type::tvoid; |
| result = exp; |
| } |
| |
| void visit(DotIdExp *exp) |
| { |
| Expression *e = semanticY(exp, sc, 1); |
| if (e && isDotOpDispatch(e)) |
| { |
| unsigned errors = global.startGagging(); |
| e = resolvePropertiesX(sc, e); |
| if (global.endGagging(errors)) |
| e = NULL; /* fall down to UFCS */ |
| else |
| { |
| result = e; |
| return; |
| } |
| } |
| if (!e) // if failed to find the property |
| { |
| /* If ident is not a valid property, rewrite: |
| * e1.ident |
| * as: |
| * .ident(e1) |
| */ |
| e = resolveUFCSProperties(sc, exp); |
| } |
| result = e; |
| } |
| |
| void visit(DotTemplateExp *e) |
| { |
| if (Expression *ex = unaSemantic(e, sc)) |
| { |
| result = ex; |
| return; |
| } |
| result = e; |
| } |
| |
| void visit(DotVarExp *exp) |
| { |
| if (exp->type) |
| { |
| result = exp; |
| return; |
| } |
| |
| exp->var = exp->var->toAlias()->isDeclaration(); |
| |
| exp->e1 = semantic(exp->e1, sc); |
| |
| if (TupleDeclaration *tup = exp->var->isTupleDeclaration()) |
| { |
| /* Replace: |
| * e1.tuple(a, b, c) |
| * with: |
| * tuple(e1.a, e1.b, e1.c) |
| */ |
| Expression *e0 = NULL; |
| Expression *ev = sc->func ? extractSideEffect(sc, "__tup", &e0, exp->e1) : exp->e1; |
| |
| Expressions *exps = new Expressions; |
| exps->reserve(tup->objects->dim); |
| for (size_t i = 0; i < tup->objects->dim; i++) |
| { |
| RootObject *o = (*tup->objects)[i]; |
| Expression *e; |
| if (o->dyncast() == DYNCAST_EXPRESSION) |
| { |
| e = (Expression *)o; |
| if (e->op == TOKdsymbol) |
| { |
| Dsymbol *s = ((DsymbolExp *)e)->s; |
| e = new DotVarExp(exp->loc, ev, s->isDeclaration()); |
| } |
| } |
| else if (o->dyncast() == DYNCAST_DSYMBOL) |
| { |
| e = new DsymbolExp(exp->loc, (Dsymbol *)o); |
| } |
| else if (o->dyncast() == DYNCAST_TYPE) |
| { |
| e = new TypeExp(exp->loc, (Type *)o); |
| } |
| else |
| { |
| exp->error("%s is not an expression", o->toChars()); |
| return setError(); |
| } |
| exps->push(e); |
| } |
| |
| Expression *e = new TupleExp(exp->loc, e0, exps); |
| e = semantic(e, sc); |
| result = e; |
| return; |
| } |
| |
| exp->e1 = exp->e1->addDtorHook(sc); |
| |
| Type *t1 = exp->e1->type; |
| |
| if (FuncDeclaration *fd = exp->var->isFuncDeclaration()) |
| { |
| // for functions, do checks after overload resolution |
| if (!fd->functionSemantic()) |
| return setError(); |
| |
| /* Bugzilla 13843: If fd obviously has no overloads, we should |
| * normalize AST, and it will give a chance to wrap fd with FuncExp. |
| */ |
| if (fd->isNested() || fd->isFuncLiteralDeclaration()) |
| { |
| // (e1, fd) |
| Expression *e = resolve(exp->loc, sc, fd, false); |
| result = Expression::combine(exp->e1, e); |
| return; |
| } |
| |
| exp->type = fd->type; |
| assert(exp->type); |
| } |
| else if (exp->var->isOverDeclaration()) |
| { |
| exp->type = Type::tvoid; // ambiguous type? |
| } |
| else |
| { |
| exp->type = exp->var->type; |
| if (!exp->type && global.errors) |
| { |
| // var is goofed up, just return 0 |
| return setError(); |
| } |
| assert(exp->type); |
| |
| if (t1->ty == Tpointer) |
| t1 = t1->nextOf(); |
| |
| exp->type = exp->type->addMod(t1->mod); |
| |
| Dsymbol *vparent = exp->var->toParent(); |
| AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL; |
| |
| if (Expression *e1x = getRightThis(exp->loc, sc, ad, exp->e1, exp->var, 1)) |
| exp->e1 = e1x; |
| else |
| { |
| /* Later checkRightThis will report correct error for invalid field variable access. |
| */ |
| Expression *e = new VarExp(exp->loc, exp->var); |
| e = semantic(e, sc); |
| result = e; |
| return; |
| } |
| checkAccess(exp->loc, sc, exp->e1, exp->var); |
| |
| VarDeclaration *v = exp->var->isVarDeclaration(); |
| if (v && (v->isDataseg() || (v->storage_class & STCmanifest))) |
| { |
| Expression *e = expandVar(WANTvalue, v); |
| if (e) |
| { |
| result = e; |
| return; |
| } |
| } |
| |
| if (v && v->isDataseg()) // fix bugzilla 8238 |
| { |
| // (e1, v) |
| checkAccess(exp->loc, sc, exp->e1, v); |
| Expression *e = new VarExp(exp->loc, v); |
| e = new CommaExp(exp->loc, exp->e1, e); |
| e = semantic(e, sc); |
| result = e; |
| return; |
| } |
| } |
| |
| //printf("-DotVarExp::semantic('%s')\n", exp->toChars()); |
| result = exp; |
| } |
| |
| void visit(DotTemplateInstanceExp *exp) |
| { |
| // Indicate we need to resolve by UFCS. |
| Expression *e = semanticY(exp, sc, 1); |
| if (!e) |
| e = resolveUFCSProperties(sc, exp); |
| result = e; |
| } |
| |
| void visit(DelegateExp *e) |
| { |
| if (e->type) |
| { |
| result = e; |
| return; |
| } |
| |
| e->e1 = semantic(e->e1, sc); |
| e->type = new TypeDelegate(e->func->type); |
| e->type = e->type->semantic(e->loc, sc); |
| FuncDeclaration *f = e->func->toAliasFunc(); |
| AggregateDeclaration *ad = f->toParent()->isAggregateDeclaration(); |
| if (f->needThis()) |
| e->e1 = getRightThis(e->loc, sc, ad, e->e1, f); |
| if (f->type->ty == Tfunction) |
| { |
| TypeFunction *tf = (TypeFunction *)f->type; |
| if (!MODmethodConv(e->e1->type->mod, f->type->mod)) |
| { |
| OutBuffer thisBuf, funcBuf; |
| MODMatchToBuffer(&thisBuf, e->e1->type->mod, tf->mod); |
| MODMatchToBuffer(&funcBuf, tf->mod, e->e1->type->mod); |
| e->error("%smethod %s is not callable using a %s%s", |
| funcBuf.peekString(), f->toPrettyChars(), thisBuf.peekString(), e->e1->toChars()); |
| return setError(); |
| } |
| } |
| if (ad && ad->isClassDeclaration() && ad->type != e->e1->type) |
| { |
| // A downcast is required for interfaces, see Bugzilla 3706 |
| e->e1 = new CastExp(e->loc, e->e1, ad->type); |
| e->e1 = semantic(e->e1, sc); |
| } |
| result = e; |
| } |
| |
| void visit(DotTypeExp *exp) |
| { |
| if (exp->type) |
| { |
| result = exp; |
| return; |
| } |
| |
| if (Expression *e = unaSemantic(exp, sc)) |
| { |
| result = e; |
| return; |
| } |
| |
| exp->type = exp->sym->getType()->addMod(exp->e1->type->mod); |
| result = exp; |
| } |
| |
| void visit(CallExp *exp) |
| { |
| if (exp->type) |
| { |
| result = exp; // semantic() already run |
| return; |
| } |
| |
| Type *t1; |
| Objects *tiargs = NULL; // initial list of template arguments |
| Expression *ethis = NULL; |
| Type *tthis = NULL; |
| Expression *e1org = exp->e1; |
| |
| if (exp->e1->op == TOKcomma) |
| { |
| /* Rewrite (a,b)(args) as (a,(b(args))) |
| */ |
| CommaExp *ce = (CommaExp *)exp->e1; |
| exp->e1 = ce->e2; |
| ce->e2 = exp; |
| result = semantic(ce, sc); |
| return; |
| } |
| |
| if (exp->e1->op == TOKdelegate) |
| { |
| DelegateExp *de = (DelegateExp *)exp->e1; |
| exp->e1 = new DotVarExp(de->loc, de->e1, de->func, de->hasOverloads); |
| result = semantic(exp, sc); |
| return; |
| } |
| |
| if (exp->e1->op == TOKfunction) |
| { |
| if (arrayExpressionSemantic(exp->arguments, sc) || |
| preFunctionParameters(sc, exp->arguments)) |
| { |
| return setError(); |
| } |
| |
| // Run e1 semantic even if arguments have any errors |
| FuncExp *fe = (FuncExp *)exp->e1; |
| exp->e1 = callExpSemantic(fe, sc, exp->arguments); |
| if (exp->e1->op == TOKerror) |
| { |
| result = exp->e1; |
| return; |
| } |
| } |
| |
| if (Expression *ex = resolveUFCS(sc, exp)) |
| { |
| result = ex; |
| return; |
| } |
| |
| /* This recognizes: |
| * foo!(tiargs)(funcargs) |
| */ |
| if (exp->e1->op == TOKscope) |
| { |
| ScopeExp *se = (ScopeExp *)exp->e1; |
| TemplateInstance *ti = se->sds->isTemplateInstance(); |
| if (ti) |
| { |
| /* Attempt to instantiate ti. If that works, go with it. |
| * If not, go with partial explicit specialization. |
| */ |
| WithScopeSymbol *withsym; |
| if (!ti->findTempDecl(sc, &withsym) || |
| !ti->semanticTiargs(sc)) |
| { |
| return setError(); |
| } |
| if (withsym && withsym->withstate->wthis) |
| { |
| exp->e1 = new VarExp(exp->e1->loc, withsym->withstate->wthis); |
| exp->e1 = new DotTemplateInstanceExp(exp->e1->loc, exp->e1, ti); |
| goto Ldotti; |
| } |
| if (ti->needsTypeInference(sc, 1)) |
| { |
| /* Go with partial explicit specialization |
| */ |
| tiargs = ti->tiargs; |
| assert(ti->tempdecl); |
| if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration()) |
| exp->e1 = new TemplateExp(exp->loc, td); |
| else if (OverDeclaration *od = ti->tempdecl->isOverDeclaration()) |
| exp->e1 = new VarExp(exp->loc,
|