void AliasThis::semantic(Scope *sc) { Dsymbol *parent = sc->parent; if (parent) parent = parent->pastMixin(); AggregateDeclaration *ad = NULL; if (parent) ad = parent->isAggregateDeclaration(); if (ad) { assert(ad->members); Dsymbol *s = ad->search(loc, ident, 0); if (!s) { s = sc->search(loc, ident, 0); if (s) ::error(loc, "%s is not a member of %s", s->toChars(), ad->toChars()); else ::error(loc, "undefined identifier %s", ident->toChars()); } else if (ad->aliasthis && s != ad->aliasthis) error("there can be only one alias this"); ad->aliasthis = s; } else error("alias this can only appear in struct or class declaration, not %s", parent ? parent->toChars() : "nowhere"); }
void AliasThis::semantic(Scope *sc) { Dsymbol *p = sc->parent->pastMixin(); AggregateDeclaration *ad = p->isAggregateDeclaration(); if (!ad) { ::error(loc, "alias this can only be a member of aggregate, not %s %s", p->kind(), p->toChars()); return; } assert(ad->members); Dsymbol *s = ad->search(loc, ident); if (!s) { s = sc->search(loc, ident, NULL); if (s) ::error(loc, "%s is not a member of %s", s->toChars(), ad->toChars()); else ::error(loc, "undefined identifier %s", ident->toChars()); return; } else if (ad->aliasthis && s != ad->aliasthis) { ::error(loc, "there can be only one alias this"); return; } if (ad->type->ty == Tstruct && ((TypeStruct *)ad->type)->sym != ad) { AggregateDeclaration *ad2 = ((TypeStruct *)ad->type)->sym; assert(ad2->type == Type::terror); ad->aliasthis = ad2->aliasthis; return; } /* disable the alias this conversion so the implicit conversion check * doesn't use it. */ ad->aliasthis = NULL; Dsymbol *sx = s; if (sx->isAliasDeclaration()) sx = sx->toAlias(); Declaration *d = sx->isDeclaration(); if (d && !d->isTupleDeclaration()) { Type *t = d->type; assert(t); if (ad->type->implicitConvTo(t) > MATCHnomatch) { ::error(loc, "alias this is not reachable as %s already converts to %s", ad->toChars(), t->toChars()); } } ad->aliasthis = s; }
Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) { //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags); if (ident == Id::dollar) { VarDeclaration **pvar; Expression *ce; L1: if (td) { /* $ gives the number of elements in the tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(Loc(), td->objects->dim, Type::tsize_t); v->init = new ExpInitializer(Loc(), e); v->storage_class |= STCstatic | STCconst; v->semantic(sc); return v; } if (type) { /* $ gives the number of type entries in the type tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(Loc(), type->arguments->dim, Type::tsize_t); v->init = new ExpInitializer(Loc(), e); v->storage_class |= STCstatic | STCconst; v->semantic(sc); return v; } if (exp->op == TOKindex) { /* array[index] where index is some function of $ */ IndexExp *ie = (IndexExp *)exp; pvar = &ie->lengthVar; ce = ie->e1; } else if (exp->op == TOKslice) { /* array[lwr .. upr] where lwr or upr is some function of $ */ SliceExp *se = (SliceExp *)exp; pvar = &se->lengthVar; ce = se->e1; } else if (exp->op == TOKarray) { /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) */ ArrayExp *ae = (ArrayExp *)exp; pvar = &ae->lengthVar; ce = ae->e1; } else /* Didn't find $, look in enclosing scope(s). */ return NULL; while (ce->op == TOKcomma) ce = ((CommaExp *)ce)->e2; /* If we are indexing into an array that is really a type * tuple, rewrite this as an index into a type tuple and * try again. */ if (ce->op == TOKtype) { Type *t = ((TypeExp *)ce)->type; if (t->ty == Ttuple) { type = (TypeTuple *)t; goto L1; } } /* *pvar is lazily initialized, so if we refer to $ * multiple times, it gets set only once. */ if (!*pvar) // if not already initialized { /* Create variable v and set it to the value of $ */ VarDeclaration *v; Type *t; if (ce->op == TOKtuple) { /* It is for an expression tuple, so the * length will be a const. */ Expression *e = new IntegerExp(Loc(), ((TupleExp *)ce)->exps->dim, Type::tsize_t); v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, new ExpInitializer(Loc(), e)); v->storage_class |= STCstatic | STCconst; } else if (ce->type && (t = ce->type->toBasetype()) != NULL && (t->ty == Tstruct || t->ty == Tclass)) { // Look for opDollar assert(exp->op == TOKarray || exp->op == TOKslice); AggregateDeclaration *ad = NULL; if (t->ty == Tclass) { ad = ((TypeClass *)t)->sym; } else if (t->ty == Tstruct) { ad = ((TypeStruct *)t)->sym; } assert(ad); Dsymbol *s = ad->search(loc, Id::opDollar, 0); if (!s) // no dollar exists -- search in higher scope return NULL; s = s->toAlias(); Expression *e = NULL; // Check for multi-dimensional opDollar(dim) template. if (TemplateDeclaration *td = s->isTemplateDeclaration()) { dinteger_t dim; if (exp->op == TOKarray) { dim = ((ArrayExp *)exp)->currentDimension; } else if (exp->op == TOKslice) { dim = 0; // slices are currently always one-dimensional } Objects *tdargs = new Objects(); Expression *edim = new IntegerExp(Loc(), dim, Type::tsize_t); edim = edim->semantic(sc); tdargs->push(edim); //TemplateInstance *ti = new TemplateInstance(loc, td, tdargs); //ti->semantic(sc); e = new DotTemplateInstanceExp(loc, ce, td->ident, tdargs); } else { /* opDollar exists, but it's not a template. * This is acceptable ONLY for single-dimension indexing. * Note that it's impossible to have both template & function opDollar, * because both take no arguments. */ if (exp->op == TOKarray && ((ArrayExp *)exp)->arguments->dim != 1) { exp->error("%s only defines opDollar for one dimension", ad->toChars()); return NULL; } Declaration *d = s->isDeclaration(); assert(d); e = new DotVarExp(loc, ce, d); } e = e->semantic(sc); if (!e->type) exp->error("%s has no value", e->toChars()); t = e->type->toBasetype(); if (t && t->ty == Tfunction) e = new CallExp(e->loc, e); v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(Loc(), e)); } else { /* For arrays, $ will either be a compile-time constant * (in which case its value in set during constant-folding), * or a variable (in which case an expression is created in * toir.c). */ VoidInitializer *e = new VoidInitializer(Loc()); e->type = Type::tsize_t; v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, e); v->storage_class |= STCctfe; // it's never a true static variable } *pvar = v; } (*pvar)->semantic(sc); return (*pvar); } return NULL; }
int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) { if (!arguments || !arguments->dim) return 0; if (sapply) // prefer opApply { for (size_t u = 0; u < arguments->dim; u++) { Parameter *arg = (*arguments)[u]; if (arg->type) { arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); } } Expression *ethis; Type *tab = aggr->type->toBasetype(); if (tab->ty == Tclass || tab->ty == Tstruct) ethis = aggr; else { assert(tab->ty == Tdelegate && aggr->op == TOKdelegate); ethis = ((DelegateExp *)aggr)->e1; } /* Look for like an * int opApply(int delegate(ref Type [, ...]) dg); * overload */ FuncDeclaration *fd = sapply->isFuncDeclaration(); if (fd) { sapply = inferApplyArgTypesX(ethis, fd, arguments); } #if 0 TemplateDeclaration *td = sapply->isTemplateDeclaration(); if (td) { inferApplyArgTypesZ(td, arguments); } #endif return sapply ? 1 : 0; } /* Return if no arguments need types. */ for (size_t u = 0; u < arguments->dim; u++) { Parameter *arg = (*arguments)[u]; if (!arg->type) break; } AggregateDeclaration *ad; Parameter *arg = (*arguments)[0]; Type *taggr = aggr->type; assert(taggr); Type *tab = taggr->toBasetype(); switch (tab->ty) { case Tarray: case Tsarray: case Ttuple: if (arguments->dim == 2) { if (!arg->type) { arg->type = Type::tsize_t; // key type arg->type = arg->type->addStorageClass(arg->storageClass); } arg = (*arguments)[1]; } if (!arg->type && tab->ty != Ttuple) { arg->type = tab->nextOf(); // value type arg->type = arg->type->addStorageClass(arg->storageClass); } break; case Taarray: { TypeAArray *taa = (TypeAArray *)tab; if (arguments->dim == 2) { if (!arg->type) { arg->type = taa->index; // key type arg->type = arg->type->addStorageClass(arg->storageClass); } arg = (*arguments)[1]; } if (!arg->type) { arg->type = taa->next; // value type arg->type = arg->type->addStorageClass(arg->storageClass); } break; } case Tclass: ad = ((TypeClass *)tab)->sym; goto Laggr; case Tstruct: ad = ((TypeStruct *)tab)->sym; goto Laggr; Laggr: if (arguments->dim == 1) { if (!arg->type) { /* Look for a front() or back() overload */ Identifier *id = (op == TOKforeach) ? Id::Ffront : Id::Fback; Dsymbol *s = ad->search(Loc(), id, 0); FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; if (fd) { // Resolve inout qualifier of front type arg->type = fd->type->nextOf(); if (arg->type) { arg->type = arg->type->substWildTo(tab->mod); arg->type = arg->type->addStorageClass(arg->storageClass); } } else if (s && s->isTemplateDeclaration()) ; else if (s && s->isDeclaration()) arg->type = ((Declaration *)s)->type; else break; } break; } break; case Tdelegate: { if (!inferApplyArgTypesY((TypeFunction *)tab->nextOf(), arguments)) return 0; break; } default: break; // ignore error, caught later } return 1; }
int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) { Identifier *idapply = (op == TOKforeach) ? Id::apply : Id::applyReverse; #if DMDV2 Identifier *idfront = (op == TOKforeach) ? Id::Ffront : Id::Fback; int sliced = 0; #endif Type *tab; Type *att = NULL; Expression *org_aggr = aggr; AggregateDeclaration *ad; while (1) { aggr = aggr->semantic(sc); aggr = resolveProperties(sc, aggr); aggr = aggr->optimize(WANTvalue); if (!aggr->type) goto Lerr; tab = aggr->type->toBasetype(); if (att == tab) { aggr = org_aggr; goto Lerr; } switch (tab->ty) { case Tarray: case Tsarray: case Ttuple: case Taarray: break; case Tclass: ad = ((TypeClass *)tab)->sym; goto Laggr; case Tstruct: ad = ((TypeStruct *)tab)->sym; goto Laggr; Laggr: #if DMDV2 if (!sliced) { sapply = search_function(ad, idapply); if (sapply) { // opApply aggregate break; } Dsymbol *s = search_function(ad, Id::slice); if (s) { Expression *rinit = new SliceExp(aggr->loc, aggr, NULL, NULL); rinit = rinit->trySemantic(sc); if (rinit) // if application of [] succeeded { aggr = rinit; sliced = 1; continue; } } } if (Dsymbol *shead = ad->search(Loc(), idfront, 0)) { // range aggregate break; } if (ad->aliasthis) { if (!att && tab->checkAliasThisRec()) att = tab; aggr = new DotIdExp(aggr->loc, aggr, ad->aliasthis->ident); continue; } #else sapply = search_function(ad, idapply); if (sapply) { // opApply aggregate break; } #endif goto Lerr; case Tdelegate: if (aggr->op == TOKdelegate) { DelegateExp *de = (DelegateExp *)aggr; sapply = de->func->isFuncDeclaration(); } break; case Terror: break; default: goto Lerr; } break; } return 1; Lerr: return 0; }