Mercurial > projects > ldc
view dmd2/template.c @ 1168:ab186e535e72
A different fix to #218 and DMD2682 that does not lead to constant folding regressions.
Fixes run/const_15, run/c/const_16_B.
The price is removing the lvalueness of struct literals. If it turns out too
much code depends on this behavior or we don't want to break with DMD, we
could keep struct literals as lvalues and instead convert struct literals used
as expression initializers into struct initializers.
author | Christian Kamm <kamm incasoftware de> |
---|---|
date | Sun, 29 Mar 2009 11:43:45 +0200 |
parents | 5fa3e0ea06e9 |
children | 638d16625da2 |
line wrap: on
line source
// Compiler implementation of the D programming language // Copyright (c) 1999-2008 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. // Handle template implementation #include <stdio.h> #include <assert.h> #if !IN_LLVM #endif #include "root.h" #include "mem.h" #include "stringtable.h" #include "mars.h" #include "identifier.h" #include "mtype.h" #include "template.h" #include "init.h" #include "expression.h" #include "scope.h" #include "module.h" #include "aggregate.h" #include "declaration.h" #include "dsymbol.h" #include "hdrgen.h" #if WINDOWS_SEH #include <windows.h> long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep); #endif #define LOG 0 /******************************************** * These functions substitute for dynamic_cast. dynamic_cast does not work * on earlier versions of gcc. */ Expression *isExpression(Object *o) { //return dynamic_cast<Expression *>(o); if (!o || o->dyncast() != DYNCAST_EXPRESSION) return NULL; return (Expression *)o; } Dsymbol *isDsymbol(Object *o) { //return dynamic_cast<Dsymbol *>(o); if (!o || o->dyncast() != DYNCAST_DSYMBOL) return NULL; return (Dsymbol *)o; } Type *isType(Object *o) { //return dynamic_cast<Type *>(o); if (!o || o->dyncast() != DYNCAST_TYPE) return NULL; return (Type *)o; } Tuple *isTuple(Object *o) { //return dynamic_cast<Tuple *>(o); if (!o || o->dyncast() != DYNCAST_TUPLE) return NULL; return (Tuple *)o; } /*********************** * Try to get arg as a type. */ Type *getType(Object *o) { Type *t = isType(o); if (!t) { Expression *e = isExpression(o); if (e) t = e->type; } return t; } Dsymbol *getDsymbol(Object *oarg) { Dsymbol *sa; Expression *ea = isExpression(oarg); if (ea) { // Try to convert Expression to symbol if (ea->op == TOKvar) sa = ((VarExp *)ea)->var; else if (ea->op == TOKfunction) sa = ((FuncExp *)ea)->fd; else sa = NULL; } else { // Try to convert Type to symbol Type *ta = isType(oarg); if (ta) sa = ta->toDsymbol(NULL); else sa = isDsymbol(oarg); // if already a symbol } return sa; } /****************************** * If o1 matches o2, return 1. * Else, return 0. */ int match(Object *o1, Object *o2, TemplateDeclaration *tempdecl, Scope *sc) { Type *t1 = isType(o1); Type *t2 = isType(o2); Expression *e1 = isExpression(o1); Expression *e2 = isExpression(o2); Dsymbol *s1 = isDsymbol(o1); Dsymbol *s2 = isDsymbol(o2); Tuple *v1 = isTuple(o1); Tuple *v2 = isTuple(o2); //printf("\t match t1 %p t2 %p, e1 %p e2 %p, s1 %p s2 %p, v1 %p v2 %p\n", t1,t2,e1,e2,s1,s2,v1,v2); /* A proper implementation of the various equals() overrides * should make it possible to just do o1->equals(o2), but * we'll do that another day. */ if (t1) { /* if t1 is an instance of ti, then give error * about recursive expansions. */ Dsymbol *s = t1->toDsymbol(sc); if (s && s->parent) { TemplateInstance *ti1 = s->parent->isTemplateInstance(); if (ti1 && ti1->tempdecl == tempdecl) { for (Scope *sc1 = sc; sc1; sc1 = sc1->enclosing) { if (sc1->scopesym == ti1) { error("recursive template expansion for template argument %s", t1->toChars()); return 1; // fake a match } } } } if (!t2 || !t1->equals(t2)) goto Lnomatch; } else if (e1) { #if 0 if (e1 && e2) { printf("match %d\n", e1->equals(e2)); e1->print(); e2->print(); e1->type->print(); e2->type->print(); } #endif if (!e2) goto Lnomatch; if (!e1->equals(e2)) goto Lnomatch; } else if (s1) { //printf("%p %s, %p %s\n", s1, s1->toChars(), s2, s2->toChars()); if (!s2 || !s1->equals(s2) || s1->parent != s2->parent) { goto Lnomatch; } VarDeclaration *v1 = s1->isVarDeclaration(); VarDeclaration *v2 = s2->isVarDeclaration(); if (v1 && v2 && v1->storage_class & v2->storage_class & STCmanifest) { ExpInitializer *ei1 = v1->init->isExpInitializer(); ExpInitializer *ei2 = v2->init->isExpInitializer(); if (ei1 && ei2 && !ei1->exp->equals(ei2->exp)) goto Lnomatch; } } else if (v1) { if (!v2) goto Lnomatch; if (v1->objects.dim != v2->objects.dim) goto Lnomatch; for (size_t i = 0; i < v1->objects.dim; i++) { if (!match((Object *)v1->objects.data[i], (Object *)v2->objects.data[i], tempdecl, sc)) goto Lnomatch; } } //printf("match\n"); return 1; // match Lnomatch: //printf("nomatch\n"); return 0; // nomatch; } /**************************************** */ void ObjectToCBuffer(OutBuffer *buf, HdrGenState *hgs, Object *oarg) { //printf("ObjectToCBuffer()\n"); Type *t = isType(oarg); Expression *e = isExpression(oarg); Dsymbol *s = isDsymbol(oarg); Tuple *v = isTuple(oarg); if (t) { //printf("\tt: %s ty = %d\n", t->toChars(), t->ty); t->toCBuffer(buf, NULL, hgs); } else if (e) e->toCBuffer(buf, hgs); else if (s) { char *p = s->ident ? s->ident->toChars() : s->toChars(); buf->writestring(p); } else if (v) { Objects *args = &v->objects; for (size_t i = 0; i < args->dim; i++) { if (i) buf->writeByte(','); Object *o = (Object *)args->data[i]; ObjectToCBuffer(buf, hgs, o); } } else if (!oarg) { buf->writestring("NULL"); } else { #ifdef DEBUG printf("bad Object = %p\n", oarg); #endif assert(0); } } Object *objectSyntaxCopy(Object *o) { if (!o) return NULL; Type *t = isType(o); if (t) return t->syntaxCopy(); Expression *e = isExpression(o); if (e) return e->syntaxCopy(); return o; } /* ======================== TemplateDeclaration ============================= */ TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id, TemplateParameters *parameters, Expression *constraint, Array *decldefs) : ScopeDsymbol(id) { #if LOG printf("TemplateDeclaration(this = %p, id = '%s')\n", this, id->toChars()); #endif #if 0 if (parameters) for (int i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; //printf("\tparameter[%d] = %p\n", i, tp); TemplateTypeParameter *ttp = tp->isTemplateTypeParameter(); if (ttp) { printf("\tparameter[%d] = %s : %s\n", i, tp->ident->toChars(), ttp->specType ? ttp->specType->toChars() : ""); } } #endif this->loc = loc; this->parameters = parameters; this->origParameters = parameters; this->constraint = constraint; this->members = decldefs; this->overnext = NULL; this->overroot = NULL; this->scope = NULL; this->onemember = NULL; } Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *) { //printf("TemplateDeclaration::syntaxCopy()\n"); TemplateDeclaration *td; TemplateParameters *p; Array *d; p = NULL; if (parameters) { p = new TemplateParameters(); p->setDim(parameters->dim); for (int i = 0; i < p->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; p->data[i] = (void *)tp->syntaxCopy(); } } Expression *e = NULL; if (constraint) e = constraint->syntaxCopy(); d = Dsymbol::arraySyntaxCopy(members); td = new TemplateDeclaration(loc, ident, p, e, d); // LDC td->intrinsicName = intrinsicName; return td; } void TemplateDeclaration::semantic(Scope *sc) { #if LOG printf("TemplateDeclaration::semantic(this = %p, id = '%s')\n", this, ident->toChars()); #endif if (scope) return; // semantic() already run if (sc->func) { // error("cannot declare template at function scope %s", sc->func->toChars()); } if (/*global.params.useArrayBounds &&*/ sc->module) { // Generate this function as it may be used // when template is instantiated in other modules sc->module->toModuleArray(); } if (/*global.params.useAssert &&*/ sc->module) { // Generate this function as it may be used // when template is instantiated in other modules sc->module->toModuleAssert(); } /* Remember Scope for later instantiations, but make * a copy since attributes can change. */ this->scope = new Scope(*sc); this->scope->setNoFree(); // Set up scope for parameters ScopeDsymbol *paramsym = new ScopeDsymbol(); paramsym->parent = sc->parent; Scope *paramscope = sc->push(paramsym); paramscope->parameterSpecialization = 1; if (global.params.doDocComments) { origParameters = new TemplateParameters(); origParameters->setDim(parameters->dim); for (int i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; origParameters->data[i] = (void *)tp->syntaxCopy(); } } for (int i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; tp->declareParameter(paramscope); } for (int i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; tp->semantic(paramscope); if (i + 1 != parameters->dim && tp->isTemplateTupleParameter()) error("template tuple parameter must be last one"); } paramscope->pop(); if (members) { Dsymbol *s; if (Dsymbol::oneMembers(members, &s)) { if (s && s->ident && s->ident->equals(ident)) { onemember = s; s->parent = this; } } } /* BUG: should check: * o no virtual functions or non-static data members of classes */ } const char *TemplateDeclaration::kind() { return (onemember && onemember->isAggregateDeclaration()) ? onemember->kind() : (char *)"template"; } /********************************** * Overload existing TemplateDeclaration 'this' with the new one 's'. * Return !=0 if successful; i.e. no conflict. */ int TemplateDeclaration::overloadInsert(Dsymbol *s) { TemplateDeclaration **pf; TemplateDeclaration *f; #if LOG printf("TemplateDeclaration::overloadInsert('%s')\n", s->toChars()); #endif f = s->isTemplateDeclaration(); if (!f) return FALSE; TemplateDeclaration *pthis = this; for (pf = &pthis; *pf; pf = &(*pf)->overnext) { #if 0 // Conflict if TemplateParameter's match // Will get caught anyway later with TemplateInstance, but // should check it now. TemplateDeclaration *f2 = *pf; if (f->parameters->dim != f2->parameters->dim) goto Lcontinue; for (int i = 0; i < f->parameters->dim; i++) { TemplateParameter *p1 = (TemplateParameter *)f->parameters->data[i]; TemplateParameter *p2 = (TemplateParameter *)f2->parameters->data[i]; if (!p1->overloadMatch(p2)) goto Lcontinue; } #if LOG printf("\tfalse: conflict\n"); #endif return FALSE; Lcontinue: ; #endif } f->overroot = this; *pf = f; #if LOG printf("\ttrue: no conflict\n"); #endif return TRUE; } /*************************************** * Given that ti is an instance of this TemplateDeclaration, * deduce the types of the parameters to this, and store * those deduced types in dedtypes[]. * Input: * flag 1: don't do semantic() because of dummy types * 2: don't change types in matchArg() * Output: * dedtypes deduced arguments * Return match level. */ MATCH TemplateDeclaration::matchWithInstance(TemplateInstance *ti, Objects *dedtypes, int flag) { MATCH m; int dedtypes_dim = dedtypes->dim; #define LOGM 0 #if LOGM printf("\n+TemplateDeclaration::matchWithInstance(this = %s, ti = %s, flag = %d)\n", toChars(), ti->toChars(), flag); #endif #if 0 printf("dedtypes->dim = %d, parameters->dim = %d\n", dedtypes_dim, parameters->dim); if (ti->tiargs->dim) printf("ti->tiargs->dim = %d, [0] = %p\n", ti->tiargs->dim, ti->tiargs->data[0]); #endif dedtypes->zero(); int parameters_dim = parameters->dim; int variadic = isVariadic() != NULL; // If more arguments than parameters, no match if (ti->tiargs->dim > parameters_dim && !variadic) { #if LOGM printf(" no match: more arguments than parameters\n"); #endif return MATCHnomatch; } assert(dedtypes_dim == parameters_dim); assert(dedtypes_dim >= ti->tiargs->dim || variadic); // Set up scope for parameters assert((size_t)scope > 0x10000); ScopeDsymbol *paramsym = new ScopeDsymbol(); paramsym->parent = scope->parent; Scope *paramscope = scope->push(paramsym); // Attempt type deduction m = MATCHexact; for (int i = 0; i < dedtypes_dim; i++) { MATCH m2; TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; Declaration *sparam; //printf("\targument [%d]\n", i); #if LOGM //printf("\targument [%d] is %s\n", i, oarg ? oarg->toChars() : "null"); TemplateTypeParameter *ttp = tp->isTemplateTypeParameter(); if (ttp) printf("\tparameter[%d] is %s : %s\n", i, tp->ident->toChars(), ttp->specType ? ttp->specType->toChars() : ""); #endif #if DMDV1 m2 = tp->matchArg(paramscope, ti->tiargs, i, parameters, dedtypes, &sparam); #else m2 = tp->matchArg(paramscope, ti->tiargs, i, parameters, dedtypes, &sparam, (flag & 2) ? 1 : 0); #endif //printf("\tm2 = %d\n", m2); if (m2 == MATCHnomatch) { #if 0 printf("\tmatchArg() for parameter %i failed\n", i); #endif goto Lnomatch; } if (m2 < m) m = m2; if (!flag) sparam->semantic(paramscope); if (!paramscope->insert(sparam)) goto Lnomatch; } if (!flag) { /* Any parameter left without a type gets the type of * its corresponding arg */ for (int i = 0; i < dedtypes_dim; i++) { if (!dedtypes->data[i]) { assert(i < ti->tiargs->dim); dedtypes->data[i] = ti->tiargs->data[i]; } } } if (m && constraint && !(flag & 1)) { /* Check to see if constraint is satisfied. */ Expression *e = constraint->syntaxCopy(); paramscope->flags |= SCOPEstaticif; e = e->semantic(paramscope); e = e->optimize(WANTvalue | WANTinterpret); if (e->isBool(TRUE)) ; else if (e->isBool(FALSE)) goto Lnomatch; else { e->error("constraint %s is not constant or does not evaluate to a bool", e->toChars()); } } #if LOGM // Print out the results printf("--------------------------\n"); printf("template %s\n", toChars()); printf("instance %s\n", ti->toChars()); if (m) { for (int i = 0; i < dedtypes_dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; Object *oarg; printf(" [%d]", i); if (i < ti->tiargs->dim) oarg = (Object *)ti->tiargs->data[i]; else oarg = NULL; tp->print(oarg, (Object *)dedtypes->data[i]); } } else goto Lnomatch; #endif #if LOGM printf(" match = %d\n", m); #endif goto Lret; Lnomatch: #if LOGM printf(" no match\n"); #endif m = MATCHnomatch; Lret: paramscope->pop(); #if LOGM printf("-TemplateDeclaration::matchWithInstance(this = %p, ti = %p) = %d\n", this, ti, m); #endif return m; } /******************************************** * Determine partial specialization order of 'this' vs td2. * Returns: * match this is at least as specialized as td2 * 0 td2 is more specialized than this */ MATCH TemplateDeclaration::leastAsSpecialized(TemplateDeclaration *td2) { /* This works by taking the template parameters to this template * declaration and feeding them to td2 as if it were a template * instance. * If it works, then this template is at least as specialized * as td2. */ TemplateInstance ti(0, ident); // create dummy template instance Objects dedtypes; #define LOG_LEASTAS 0 #if LOG_LEASTAS printf("%s.leastAsSpecialized(%s)\n", toChars(), td2->toChars()); #endif // Set type arguments to dummy template instance to be types // generated from the parameters to this template declaration ti.tiargs = new Objects(); ti.tiargs->setDim(parameters->dim); for (int i = 0; i < ti.tiargs->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; void *p = tp->dummyArg(); if (p) ti.tiargs->data[i] = p; else ti.tiargs->setDim(i); } // Temporary Array to hold deduced types //dedtypes.setDim(parameters->dim); dedtypes.setDim(td2->parameters->dim); // Attempt a type deduction MATCH m = td2->matchWithInstance(&ti, &dedtypes, 1); if (m) { /* A non-variadic template is more specialized than a * variadic one. */ if (isVariadic() && !td2->isVariadic()) goto L1; #if LOG_LEASTAS printf(" matches %d, so is least as specialized\n", m); #endif return m; } L1: #if LOG_LEASTAS printf(" doesn't match, so is not as specialized\n"); #endif return MATCHnomatch; } /************************************************* * Match function arguments against a specific template function. * Input: * loc instantiation location * targsi Expression/Type initial list of template arguments * ethis 'this' argument if !NULL * fargs arguments to function * Output: * dedargs Expression/Type deduced template arguments * Returns: * match level */ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Loc loc, Objects *targsi, Expression *ethis, Expressions *fargs, Objects *dedargs) { size_t i; size_t nfparams; size_t nfargs; size_t nargsi; // array size of targsi int fptupindex = -1; int tuple_dim = 0; MATCH match = MATCHexact; FuncDeclaration *fd = onemember->toAlias()->isFuncDeclaration(); TypeFunction *fdtype; // type of fd TemplateTupleParameter *tp; Objects dedtypes; // for T:T*, the dedargs is the T*, dedtypes is the T #if 0 printf("\nTemplateDeclaration::deduceFunctionTemplateMatch() %s\n", toChars()); for (i = 0; i < fargs->dim; i++) { Expression *e = (Expression *)fargs->data[i]; printf("\tfarg[%d] is %s, type is %s\n", i, e->toChars(), e->type->toChars()); } #endif assert((size_t)scope > 0x10000); dedargs->setDim(parameters->dim); dedargs->zero(); dedtypes.setDim(parameters->dim); dedtypes.zero(); // Set up scope for parameters ScopeDsymbol *paramsym = new ScopeDsymbol(); paramsym->parent = scope->parent; Scope *paramscope = scope->push(paramsym); tp = isVariadic(); nargsi = 0; if (targsi) { // Set initial template arguments nargsi = targsi->dim; if (nargsi > parameters->dim) { if (!tp) goto Lnomatch; dedargs->setDim(nargsi); dedargs->zero(); } memcpy(dedargs->data, targsi->data, nargsi * sizeof(*dedargs->data)); for (i = 0; i < nargsi; i++) { assert(i < parameters->dim); TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; MATCH m; Declaration *sparam; m = tp->matchArg(paramscope, dedargs, i, parameters, &dedtypes, &sparam); //printf("\tdeduceType m = %d\n", m); if (m == MATCHnomatch) goto Lnomatch; if (m < match) match = m; sparam->semantic(paramscope); if (!paramscope->insert(sparam)) goto Lnomatch; } } assert(fd->type->ty == Tfunction); fdtype = (TypeFunction *)fd->type; nfparams = Argument::dim(fdtype->parameters); // number of function parameters nfargs = fargs->dim; // number of function arguments /* Check for match of function arguments with variadic template * parameter, such as: * * template Foo(T, A...) { void Foo(T t, A a); } * void main() { Foo(1,2,3); } */ if (tp) // if variadic { if (nfparams == 0) // if no function parameters { Tuple *t = new Tuple(); //printf("t = %p\n", t); dedargs->data[parameters->dim - 1] = (void *)t; goto L2; } else if (nfargs < nfparams - 1) goto L1; else { /* Figure out which of the function parameters matches * the tuple template parameter. Do this by matching * type identifiers. * Set the index of this function parameter to fptupindex. */ for (fptupindex = 0; fptupindex < nfparams; fptupindex++) { Argument *fparam = (Argument *)fdtype->parameters->data[fptupindex]; if (fparam->type->ty != Tident) continue; TypeIdentifier *tid = (TypeIdentifier *)fparam->type; if (!tp->ident->equals(tid->ident) || tid->idents.dim) continue; if (fdtype->varargs) // variadic function doesn't goto Lnomatch; // go with variadic template /* The types of the function arguments * now form the tuple argument. */ Tuple *t = new Tuple(); dedargs->data[parameters->dim - 1] = (void *)t; tuple_dim = nfargs - (nfparams - 1); t->objects.setDim(tuple_dim); for (i = 0; i < tuple_dim; i++) { Expression *farg = (Expression *)fargs->data[fptupindex + i]; t->objects.data[i] = (void *)farg->type; } goto L2; } fptupindex = -1; } } L1: if (nfparams == nfargs) ; else if (nfargs > nfparams) { if (fdtype->varargs == 0) goto Lnomatch; // too many args, no match match = MATCHconvert; // match ... with a conversion } L2: // Match 'ethis' to any TemplateThisParameter's if (ethis) { for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; TemplateThisParameter *ttp = tp->isTemplateThisParameter(); if (ttp) { MATCH m; Type *t = new TypeIdentifier(0, ttp->ident); m = ethis->type->deduceType(scope, t, parameters, &dedtypes); if (!m) goto Lnomatch; if (m < match) match = m; // pick worst match } } } // Loop through the function parameters for (i = 0; i < nfparams; i++) { /* Skip over function parameters which wound up * as part of a template tuple parameter. */ if (i == fptupindex) { if (fptupindex == nfparams - 1) break; i += tuple_dim - 1; continue; } Argument *fparam = Argument::getNth(fdtype->parameters, i); if (i >= nfargs) // if not enough arguments { if (fparam->defaultArg) { /* Default arguments do not participate in template argument * deduction. */ goto Lmatch; } } else { Expression *farg = (Expression *)fargs->data[i]; #if 0 printf("\tfarg->type = %s\n", farg->type->toChars()); printf("\tfparam->type = %s\n", fparam->type->toChars()); #endif MATCH m; //m = farg->type->toHeadMutable()->deduceType(scope, fparam->type, parameters, &dedtypes); m = farg->type->deduceType(scope, fparam->type, parameters, &dedtypes); //printf("\tdeduceType m = %d\n", m); /* If no match, see if there's a conversion to a delegate */ if (!m && fparam->type->toBasetype()->ty == Tdelegate) { TypeDelegate *td = (TypeDelegate *)fparam->type->toBasetype(); TypeFunction *tf = (TypeFunction *)td->next; if (!tf->varargs && Argument::dim(tf->parameters) == 0) { m = farg->type->deduceType(scope, tf->next, parameters, &dedtypes); if (!m && tf->next->toBasetype()->ty == Tvoid) m = MATCHconvert; } //printf("\tm2 = %d\n", m); } if (m) { if (m < match) match = m; // pick worst match continue; } } /* The following code for variadic arguments closely * matches TypeFunction::callMatch() */ if (!(fdtype->varargs == 2 && i + 1 == nfparams)) goto Lnomatch; /* Check for match with function parameter T... */ Type *tb = fparam->type->toBasetype(); switch (tb->ty) { // Perhaps we can do better with this, see TypeFunction::callMatch() case Tsarray: { TypeSArray *tsa = (TypeSArray *)tb; integer_t sz = tsa->dim->toInteger(); if (sz != nfargs - i) goto Lnomatch; } case Tarray: { TypeArray *ta = (TypeArray *)tb; for (; i < nfargs; i++) { Expression *arg = (Expression *)fargs->data[i]; assert(arg); MATCH m; /* If lazy array of delegates, * convert arg(s) to delegate(s) */ Type *tret = fparam->isLazyArray(); if (tret) { if (ta->next->equals(arg->type)) { m = MATCHexact; } else { m = arg->implicitConvTo(tret); if (m == MATCHnomatch) { if (tret->toBasetype()->ty == Tvoid) m = MATCHconvert; } } } else { m = arg->type->deduceType(scope, ta->next, parameters, &dedtypes); //m = arg->implicitConvTo(ta->next); } if (m == MATCHnomatch) goto Lnomatch; if (m < match) match = m; } goto Lmatch; } case Tclass: case Tident: goto Lmatch; default: goto Lnomatch; } } Lmatch: /* Fill in any missing arguments with their defaults. */ for (i = nargsi; i < dedargs->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; //printf("tp[%d] = %s\n", i, tp->ident->toChars()); /* For T:T*, the dedargs is the T*, dedtypes is the T * But for function templates, we really need them to match */ Object *oarg = (Object *)dedargs->data[i]; Object *oded = (Object *)dedtypes.data[i]; //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded); if (!oarg) { if (oded) { if (tp->specialization()) { /* The specialization can work as long as afterwards * the oded == oarg */ Declaration *sparam; dedargs->data[i] = (void *)oded; MATCH m2 = tp->matchArg(paramscope, dedargs, i, parameters, &dedtypes, &sparam, 0); //printf("m2 = %d\n", m2); if (!m2) goto Lnomatch; if (m2 < match) match = m2; // pick worst match if (dedtypes.data[i] != oded) error("specialization not allowed for deduced parameter %s", tp->ident->toChars()); } } else { oded = tp->defaultArg(loc, paramscope); if (!oded) goto Lnomatch; } declareParameter(paramscope, tp, oded); dedargs->data[i] = (void *)oded; } } if (constraint) { /* Check to see if constraint is satisfied. */ Expression *e = constraint->syntaxCopy(); paramscope->flags |= SCOPEstaticif; e = e->semantic(paramscope); e = e->optimize(WANTvalue | WANTinterpret); if (e->isBool(TRUE)) ; else if (e->isBool(FALSE)) goto Lnomatch; else { e->error("constraint %s is not constant or does not evaluate to a bool", e->toChars()); } } #if 0 for (i = 0; i < dedargs->dim; i++) { Type *t = (Type *)dedargs->data[i]; printf("\tdedargs[%d] = %d, %s\n", i, t->dyncast(), t->toChars()); } #endif paramscope->pop(); //printf("\tmatch %d\n", match); return match; Lnomatch: paramscope->pop(); //printf("\tnomatch\n"); return MATCHnomatch; } /************************************************** * Declare template parameter tp with value o, and install it in the scope sc. */ void TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Object *o) { //printf("TemplateDeclaration::declareParameter('%s', o = %p)\n", tp->ident->toChars(), o); Type *targ = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Tuple *va = isTuple(o); Dsymbol *s; if (targ) { //printf("type %s\n", targ->toChars()); s = new AliasDeclaration(0, tp->ident, targ); } else if (sa) { //printf("Alias %s %s;\n", sa->ident->toChars(), tp->ident->toChars()); s = new AliasDeclaration(0, tp->ident, sa); } else if (ea) { // tdtypes.data[i] always matches ea here Initializer *init = new ExpInitializer(loc, ea); TemplateValueParameter *tvp = tp->isTemplateValueParameter(); Type *t = tvp ? tvp->valType : NULL; VarDeclaration *v = new VarDeclaration(loc, t, tp->ident, init); v->storage_class = STCmanifest; s = v; } else if (va) { //printf("\ttuple\n"); s = new TupleDeclaration(loc, tp->ident, &va->objects); } else { #ifdef DEBUG o->print(); #endif assert(0); } if (!sc->insert(s)) error("declaration %s is already defined", tp->ident->toChars()); s->semantic(sc); } /************************************** * Determine if TemplateDeclaration is variadic. */ TemplateTupleParameter *isVariadic(TemplateParameters *parameters) { size_t dim = parameters->dim; TemplateTupleParameter *tp = NULL; if (dim) tp = ((TemplateParameter *)parameters->data[dim - 1])->isTemplateTupleParameter(); return tp; } TemplateTupleParameter *TemplateDeclaration::isVariadic() { return ::isVariadic(parameters); } /*********************************** * We can overload templates. */ int TemplateDeclaration::isOverloadable() { return 1; } /************************************************* * Given function arguments, figure out which template function * to expand, and return that function. * If no match, give error message and return NULL. * Input: * sc instantiation scope * loc instantiation location * targsi initial list of template arguments * ethis if !NULL, the 'this' pointer argument * fargs arguments to function * flags 1: do not issue error message on no match, just return NULL */ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *fargs, int flags) { MATCH m_best = MATCHnomatch; TemplateDeclaration *td_ambig = NULL; TemplateDeclaration *td_best = NULL; Objects *tdargs = new Objects(); TemplateInstance *ti; FuncDeclaration *fd; #if 0 printf("TemplateDeclaration::deduceFunctionTemplate() %s\n", toChars()); printf(" targsi:\n"); if (targsi) { for (int i = 0; i < targsi->dim; i++) { Object *arg = (Object *)targsi->data[i]; printf("\t%s\n", arg->toChars()); } } printf(" fargs:\n"); for (int i = 0; i < fargs->dim; i++) { Expression *arg = (Expression *)fargs->data[i]; printf("\t%s %s\n", arg->type->toChars(), arg->toChars()); //printf("\tty = %d\n", arg->type->ty); } #endif for (TemplateDeclaration *td = this; td; td = td->overnext) { if (!td->scope) { error("forward reference to template %s", td->toChars()); goto Lerror; } if (!td->onemember || !td->onemember->toAlias()->isFuncDeclaration()) { error("is not a function template"); goto Lerror; } MATCH m; Objects dedargs; m = td->deduceFunctionTemplateMatch(loc, targsi, ethis, fargs, &dedargs); //printf("deduceFunctionTemplateMatch = %d\n", m); if (!m) // if no match continue; if (m < m_best) goto Ltd_best; if (m > m_best) goto Ltd; { // Disambiguate by picking the most specialized TemplateDeclaration MATCH c1 = td->leastAsSpecialized(td_best); MATCH c2 = td_best->leastAsSpecialized(td); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; else if (c1 < c2) goto Ltd_best; else goto Lambig; } Lambig: // td_best and td are ambiguous td_ambig = td; continue; Ltd_best: // td_best is the best match so far td_ambig = NULL; continue; Ltd: // td is the new best match td_ambig = NULL; assert((size_t)td->scope > 0x10000); td_best = td; m_best = m; tdargs->setDim(dedargs.dim); memcpy(tdargs->data, dedargs.data, tdargs->dim * sizeof(void *)); continue; } if (!td_best) { if (!(flags & 1)) error(loc, "does not match any function template declaration"); goto Lerror; } if (td_ambig) { error(loc, "matches more than one function template declaration:\n %s\nand:\n %s", td_best->toChars(), td_ambig->toChars()); } /* The best match is td_best with arguments tdargs. * Now instantiate the template. */ assert((size_t)td_best->scope > 0x10000); ti = new TemplateInstance(loc, td_best, tdargs); ti->semantic(sc); fd = ti->toAlias()->isFuncDeclaration(); if (!fd) goto Lerror; return fd; Lerror: if (!(flags & 1)) { HdrGenState hgs; OutBuffer bufa; Objects *args = targsi; if (args) { for (int i = 0; i < args->dim; i++) { if (i) bufa.writeByte(','); Object *oarg = (Object *)args->data[i]; ObjectToCBuffer(&bufa, &hgs, oarg); } } OutBuffer buf; argExpTypesToCBuffer(&buf, fargs, &hgs); error(loc, "cannot deduce template function from argument types !(%s)(%s)", bufa.toChars(), buf.toChars()); } return NULL; } void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { #if 0 // Should handle template functions if (onemember && onemember->isFuncDeclaration()) buf->writestring("foo "); #endif buf->writestring(kind()); buf->writeByte(' '); buf->writestring(ident->toChars()); buf->writeByte('('); for (int i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; if (hgs->ddoc) tp = (TemplateParameter *)origParameters->data[i]; if (i) buf->writeByte(','); tp->toCBuffer(buf, hgs); } buf->writeByte(')'); if (constraint) { buf->writestring(" if ("); constraint->toCBuffer(buf, hgs); buf->writeByte(')'); } if (hgs->hdrgen) { hgs->tpltMember++; buf->writenl(); buf->writebyte('{'); buf->writenl(); for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->toCBuffer(buf, hgs); } buf->writebyte('}'); buf->writenl(); hgs->tpltMember--; } } char *TemplateDeclaration::toChars() { OutBuffer buf; HdrGenState hgs; memset(&hgs, 0, sizeof(hgs)); buf.writestring(ident->toChars()); buf.writeByte('('); for (int i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; if (i) buf.writeByte(','); tp->toCBuffer(&buf, &hgs); } buf.writeByte(')'); if (constraint) { buf.writestring(" if ("); constraint->toCBuffer(&buf, &hgs); buf.writeByte(')'); } buf.writeByte(0); return (char *)buf.extractData(); } /* ======================== Type ============================================ */ /**** * Given an identifier, figure out which TemplateParameter it is. * Return -1 if not found. */ int templateIdentifierLookup(Identifier *id, TemplateParameters *parameters) { for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; if (tp->ident->equals(id)) return i; } return -1; } int templateParameterLookup(Type *tparam, TemplateParameters *parameters) { assert(tparam->ty == Tident); TypeIdentifier *tident = (TypeIdentifier *)tparam; //printf("\ttident = '%s'\n", tident->toChars()); if (tident->idents.dim == 0) { return templateIdentifierLookup(tident->ident, parameters); } return -1; } /* These form the heart of template argument deduction. * Given 'this' being the type argument to the template instance, * it is matched against the template declaration parameter specialization * 'tparam' to determine the type to be used for the parameter. * Example: * template Foo(T:T*) // template declaration * Foo!(int*) // template instantiation * Input: * this = int* * tparam = T * parameters = [ T:T* ] // Array of TemplateParameter's * Output: * dedtypes = [ int ] // Array of Expression/Type's */ MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { #if 0 printf("Type::deduceType()\n"); printf("\tthis = %d, ", ty); print(); printf("\ttparam = %d, ", tparam->ty); tparam->print(); #endif if (!tparam) goto Lnomatch; if (this == tparam) goto Lexact; if (tparam->ty == Tident) { // Determine which parameter tparam is int i = templateParameterLookup(tparam, parameters); if (i == -1) { if (!sc) goto Lnomatch; /* Need a loc to go with the semantic routine. */ Loc loc; if (parameters->dim) { TemplateParameter *tp = (TemplateParameter *)parameters->data[0]; loc = tp->loc; } /* BUG: what if tparam is a template instance, that * has as an argument another Tident? */ tparam = tparam->semantic(loc, sc); assert(tparam->ty != Tident); return deduceType(sc, tparam, parameters, dedtypes); } TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; // Found the corresponding parameter tp if (!tp->isTemplateTypeParameter()) goto Lnomatch; Type *tt = this; Type *at = (Type *)dedtypes->data[i]; // 3*3 == 9 cases if (tparam->isMutable()) { // foo(U:U) T => T // foo(U:U) const(T) => const(T) // foo(U:U) invariant(T) => invariant(T) if (!at) { dedtypes->data[i] = (void *)this; goto Lexact; } } else if (mod == tparam->mod) { // foo(U:const(U)) const(T) => T // foo(U:invariant(U)) invariant(T) => T tt = mutableOf(); if (!at) { dedtypes->data[i] = (void *)tt; goto Lexact; } } else if (tparam->isConst()) { // foo(U:const(U)) T => T // foo(U:const(U)) invariant(T) => T tt = mutableOf(); if (!at) { dedtypes->data[i] = (void *)tt; goto Lconst; } } else { // foo(U:invariant(U)) T => nomatch // foo(U:invariant(U)) const(T) => nomatch if (!at) goto Lnomatch; } if (tt->equals(at)) goto Lexact; else if (tt->ty == Tclass && at->ty == Tclass) { return tt->implicitConvTo(at); } else if (tt->ty == Tsarray && at->ty == Tarray && tt->nextOf()->implicitConvTo(at->nextOf()) >= MATCHconst) { goto Lexact; } else goto Lnomatch; } if (ty != tparam->ty) return implicitConvTo(tparam); // goto Lnomatch; if (nextOf()) return nextOf()->deduceType(sc, tparam->nextOf(), parameters, dedtypes); Lexact: return MATCHexact; Lnomatch: return MATCHnomatch; Lconst: return MATCHconst; } MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { #if 0 printf("TypeSArray::deduceType()\n"); printf("\tthis = %d, ", ty); print(); printf("\ttparam = %d, ", tparam->ty); tparam->print(); #endif // Extra check that array dimensions must match if (tparam) { if (tparam->ty == Tsarray) { TypeSArray *tp = (TypeSArray *)tparam; if (tp->dim->op == TOKvar && ((VarExp *)tp->dim)->var->storage_class & STCtemplateparameter) { int i = templateIdentifierLookup(((VarExp *)tp->dim)->var->ident, parameters); // This code matches code in TypeInstance::deduceType() if (i == -1) goto Lnomatch; TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; TemplateValueParameter *tvp = tp->isTemplateValueParameter(); if (!tvp) goto Lnomatch; Expression *e = (Expression *)dedtypes->data[i]; if (e) { if (!dim->equals(e)) goto Lnomatch; } else { Type *vt = tvp->valType->semantic(0, sc); MATCH m = (MATCH)dim->implicitConvTo(vt); if (!m) goto Lnomatch; dedtypes->data[i] = dim; } } else if (dim->toInteger() != tp->dim->toInteger()) return MATCHnomatch; } else if (tparam->ty == Taarray) { TypeAArray *tp = (TypeAArray *)tparam; if (tp->index->ty == Tident) { TypeIdentifier *tident = (TypeIdentifier *)tp->index; if (tident->idents.dim == 0) { Identifier *id = tident->ident; for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (TemplateParameter *)parameters->data[i]; if (tp->ident->equals(id)) { // Found the corresponding template parameter TemplateValueParameter *tvp = tp->isTemplateValueParameter(); if (!tvp || !tvp->valType->isintegral()) goto Lnomatch; if (dedtypes->data[i]) { if (!dim->equals((Object *)dedtypes->data[i])) goto Lnomatch; } else { dedtypes->data[i] = (void *)dim; } return next->deduceType(sc, tparam->nextOf(), parameters, dedtypes); } } } } } else if (tparam->ty == Tarray) { MATCH m; m = next->deduceType(sc, tparam->nextOf(), parameters, dedtypes); if (m == MATCHexact) m = MATCHconvert; return m; } } return Type::deduceType(sc, tparam, parameters, dedtypes); Lnomatch: return MATCHnomatch; } MATCH TypeAArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { #if 0 printf("TypeAArray::deduceType()\n"); printf("\tthis = %d, ", ty); print(); printf("\ttparam = %d, ", tparam->ty); tparam->print(); #endif // Extra check that index type must match if (tparam && tparam->ty == Taarray) { TypeAArray *tp = (TypeAArray *)tparam; if (!index->deduceType(sc, tp->index, parameters, dedtypes)) { return MATCHnomatch; } } return Type::deduceType(sc, tparam, parameters, dedtypes); } MATCH TypeFunction::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { //printf("TypeFunction::deduceType()\n"); //printf("\tthis = %d, ", ty); print(); //printf("\ttparam = %d, ", tparam->ty); tparam->print(); // Extra check that function characteristics must match if (tparam && tparam->ty == Tfunction) { TypeFunction *tp = (TypeFunction *)tparam; if (varargs != tp->varargs || linkage != tp->linkage) return MATCHnomatch; size_t nfargs = Argument::dim(this->parameters); size_t nfparams = Argument::dim(tp->parameters); /* See if tuple match */ if (nfparams > 0 && nfargs >= nfparams - 1) { /* See if 'A' of the template parameter matches 'A' * of the type of the last function parameter. */ Argument *fparam = (Argument *)tp->parameters->data[nfparams - 1]; if (fparam->type->ty != Tident) goto L1; TypeIdentifier *tid = (TypeIdentifier *)fparam->type; if (tid->idents.dim) goto L1; /* Look through parameters to find tuple matching tid->ident */ size_t tupi = 0; for (; 1; tupi++) { if (tupi == parameters->dim) goto L1; TemplateParameter *t = (TemplateParameter *)parameters->data[tupi]; TemplateTupleParameter *tup = t->isTemplateTupleParameter(); if (tup && tup->ident->equals(tid->ident)) break; } /* The types of the function arguments [nfparams - 1 .. nfargs] * now form the tuple argument. */ int tuple_dim = nfargs - (nfparams - 1); /* See if existing tuple, and whether it matches or not */ Object *o = (Object *)dedtypes->data[tupi]; if (o) { // Existing deduced argument must be a tuple, and must match Tuple *t = isTuple(o); if (!t || t->objects.dim != tuple_dim) return MATCHnomatch; for (size_t i = 0; i < tuple_dim; i++) { Argument *arg = Argument::getNth(this->parameters, nfparams - 1 + i); if (!arg->type->equals((Object *)t->objects.data[i])) return MATCHnomatch; } } else { // Create new tuple Tuple *t = new Tuple(); t->objects.setDim(tuple_dim); for (size_t i = 0; i < tuple_dim; i++) { Argument *arg = Argument::getNth(this->parameters, nfparams - 1 + i); t->objects.data[i] = (void *)arg->type; } dedtypes->data[tupi] = (void *)t; } nfparams--; // don't consider the last parameter for type deduction goto L2; } L1: if (nfargs != nfparams) return MATCHnomatch; L2: for (size_t i = 0; i < nfparams; i++) { Argument *a = Argument::getNth(this->parameters, i); Argument *ap = Argument::getNth(tp->parameters, i); if (a->storageClass != ap->storageClass || !a->type->deduceType(sc, ap->type, parameters, dedtypes)) return MATCHnomatch; } } return Type::deduceType(sc, tparam, parameters, dedtypes); } MATCH TypeIdentifier::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { // Extra check if (tparam && tparam->ty == Tident) { TypeIdentifier *tp = (TypeIdentifier *)tparam; for (int i = 0; i < idents.dim; i++) { Identifier *id1 = (Identifier *)idents.data[i]; Identifier *id2 = (Identifier *)tp->idents.data[i]; if (!id1->equals(id2)) return MATCHnomatch; } } return Type::deduceType(sc, tparam, parameters, dedtypes); } MATCH TypeInstance::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { //printf("TypeInstance::deduceType(tparam = %s) %s\n", tparam->toChars(), toChars()); //printf("\ttparam = %d, ", tparam->ty); tparam->print(); // Extra check if (tparam && tparam->ty == Tinstance) { TypeInstance *tp = (TypeInstance *)tparam; //printf("tempinst->tempdecl = %p\n", tempinst->tempdecl); //printf("tp->tempinst->tempdecl = %p\n", tp->tempinst->tempdecl); if (!tp->tempinst->tempdecl) { //printf("tp->tempinst->name = '%s'\n", tp->tempinst->name->toChars()); if (!tp->tempinst->name->equals(tempinst->name)) { /* Handle case of: * template Foo(T : sa!(T), alias sa) */ int i = templateIdentifierLookup(tp->tempinst->name, parameters); if (i == -1) { /* Didn't find it as a parameter identifier. Try looking * it up and seeing if is an alias. See Bugzilla 1454 */ Dsymbol *s = tempinst->tempdecl->scope->search(0, tp->tempinst->name, NULL); if (s) { s = s->toAlias(); TemplateDeclaration *td = s->isTemplateDeclaration(); if (td && td == tempinst->tempdecl) goto L2; } goto Lnomatch; } TemplateParameter *tpx = (TemplateParameter *)parameters->data[i]; // This logic duplicates tpx->matchArg() TemplateAliasParameter *ta = tpx->isTemplateAliasParameter(); if (!ta) goto Lnomatch; Object *sa = tempinst->tempdecl; if (!sa) goto Lnomatch; if (ta->specAlias && sa != ta->specAlias) goto Lnomatch; if (dedtypes->data[i]) { // Must match already deduced symbol Object *s = (Object *)dedtypes->data[i]; if (s != sa) goto Lnomatch; } dedtypes->data[i] = sa; } } else if (tempinst->tempdecl != tp->tempinst->tempdecl) goto Lnomatch; L2: if (tempinst->tiargs->dim != tp->tempinst->tiargs->dim) goto Lnomatch; for (int i = 0; i < tempinst->tiargs->dim; i++) { //printf("\ttest: tempinst->tiargs[%d]\n", i); int j; Object *o1 = (Object *)tempinst->tiargs->data[i]; Object *o2 = (Object *)tp->tempinst->tiargs->data[i]; Type *t1 = isType(o1); Type *t2 = isType(o2); Expression *e1 = isExpression(o1); Expression *e2 = isExpression(o2); #if 0 if (t1) printf("t1 = %s\n", t1->toChars()); if (t2) printf("t2 = %s\n", t2->toChars()); if (e1) printf("e1 = %s\n", e1->toChars()); if (e2) printf("e2 = %s\n", e2->toChars()); #endif if (t1 && t2) { if (!t1->deduceType(sc, t2, parameters, dedtypes)) goto Lnomatch; } else if (e1 && e2) { if (!e1->equals(e2)) { if (e2->op == TOKvar) { /* * (T:Number!(e2), int e2) */ j = templateIdentifierLookup(((VarExp *)e2)->var->ident, parameters); goto L1; } goto Lnomatch; } } else if (e1 && t2 && t2->ty == Tident) { j = templateParameterLookup(t2, parameters); L1: if (j == -1) goto Lnomatch; TemplateParameter *tp = (TemplateParameter *)parameters->data[j]; // BUG: use tp->matchArg() instead of the following TemplateValueParameter *tv = tp->isTemplateValueParameter(); if (!tv) goto Lnomatch; Expression *e = (Expression *)dedtypes->data[j]; if (e) { if (!e1->equals(e)) goto Lnomatch; } else { Type *vt = tv->valType->semantic(0, sc); MATCH m = (MATCH)e1->implicitConvTo(vt); if (!m) goto Lnomatch; dedtypes->data[j] = e1; } } // BUG: Need to handle alias and tuple parameters else goto Lnomatch; } } return Type::deduceType(sc, tparam, parameters, dedtypes); Lnomatch: return MATCHnomatch; } MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { //printf("TypeStruct::deduceType()\n"); //printf("\tthis->parent = %s, ", sym->parent->toChars()); print(); //printf("\ttparam = %d, ", tparam->ty); tparam->print(); /* If this struct is a template struct, and we're matching * it against a template instance, convert the struct type * to a template instance, too, and try again. */ TemplateInstance *ti = sym->parent->isTemplateInstance(); if (tparam && tparam->ty == Tinstance) { if (ti && ti->toAlias() == sym) { TypeInstance *t = new TypeInstance(0, ti); return t->deduceType(sc, tparam, parameters, dedtypes); } /* Match things like: * S!(T).foo */ TypeInstance *tpi = (TypeInstance *)tparam; if (tpi->idents.dim) { Identifier *id = (Identifier *)tpi->idents.data[tpi->idents.dim - 1]; if (id->dyncast() == DYNCAST_IDENTIFIER && sym->ident->equals(id)) { Type *tparent = sym->parent->getType(); if (tparent) { /* Slice off the .foo in S!(T).foo */ tpi->idents.dim--; MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes); tpi->idents.dim++; return m; } } } } // Extra check if (tparam && tparam->ty == Tstruct) { TypeStruct *tp = (TypeStruct *)tparam; if (sym != tp->sym) return MATCHnomatch; } return Type::deduceType(sc, tparam, parameters, dedtypes); } MATCH TypeEnum::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { // Extra check if (tparam && tparam->ty == Tenum) { TypeEnum *tp = (TypeEnum *)tparam; if (sym != tp->sym) return MATCHnomatch; } return Type::deduceType(sc, tparam, parameters, dedtypes); } MATCH TypeTypedef::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { // Extra check if (tparam && tparam->ty == Ttypedef) { TypeTypedef *tp = (TypeTypedef *)tparam; if (sym != tp->sym) return MATCHnomatch; } return Type::deduceType(sc, tparam, parameters, dedtypes); } MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) { //printf("TypeClass::deduceType(this = %s)\n", toChars()); /* If this class is a template class, and we're matching * it against a template instance, convert the class type * to a template instance, too, and try again. */ TemplateInstance *ti = sym->parent->isTemplateInstance(); if (tparam && tparam->ty == Tinstance) { if (ti && ti->toAlias() == sym) { TypeInstance *t = new TypeInstance(0, ti); return t->deduceType(sc, tparam, parameters, dedtypes); } /* Match things like: * S!(T).foo */ TypeInstance *tpi = (TypeInstance *)tparam; if (tpi->idents.dim) { Identifier *id = (Identifier *)tpi->idents.data[tpi->idents.dim - 1]; if (id->dyncast() == DYNCAST_IDENTIFIER && sym->ident->equals(id)) { Type *tparent = sym->parent->getType(); if (tparent) { /* Slice off the .foo in S!(T).foo */ tpi->idents.dim--; MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes); tpi->idents.dim++; return m; } } } } // Extra check if (tparam && tparam->ty == Tclass) { TypeClass *tp = (TypeClass *)tparam; //printf("\t%d\n", (MATCH) implicitConvTo(tp)); return implicitConvTo(tp); } return Type::deduceType(sc, tparam, parameters, dedtypes); } /* ======================== TemplateParameter =============================== */ TemplateParameter::TemplateParameter(Loc loc, Identifier *ident) { this->loc = loc; this->ident = ident; this->sparam = NULL; } TemplateTypeParameter *TemplateParameter::isTemplateTypeParameter() { return NULL; } TemplateValueParameter *TemplateParameter::isTemplateValueParameter() { return NULL; } TemplateAliasParameter *TemplateParameter::isTemplateAliasParameter() { return NULL; } TemplateTupleParameter *TemplateParameter::isTemplateTupleParameter() { return NULL; } #if DMDV2 TemplateThisParameter *TemplateParameter::isTemplateThisParameter() { return NULL; } #endif /* ======================== TemplateTypeParameter =========================== */ // type-parameter TemplateTypeParameter::TemplateTypeParameter(Loc loc, Identifier *ident, Type *specType, Type *defaultType) : TemplateParameter(loc, ident) { this->ident = ident; this->specType = specType; this->defaultType = defaultType; } TemplateTypeParameter *TemplateTypeParameter::isTemplateTypeParameter() { return this; } TemplateParameter *TemplateTypeParameter::syntaxCopy() { TemplateTypeParameter *tp = new TemplateTypeParameter(loc, ident, specType, defaultType); if (tp->specType) tp->specType = specType->syntaxCopy(); if (defaultType) tp->defaultType = defaultType->syntaxCopy(); return tp; } void TemplateTypeParameter::declareParameter(Scope *sc) { //printf("TemplateTypeParameter::declareParameter('%s')\n", ident->toChars()); TypeIdentifier *ti = new TypeIdentifier(loc, ident); sparam = new AliasDeclaration(loc, ident, ti); if (!sc->insert(sparam)) error(loc, "parameter '%s' multiply defined", ident->toChars()); } void TemplateTypeParameter::semantic(Scope *sc) { //printf("TemplateTypeParameter::semantic('%s')\n", ident->toChars()); if (specType) { specType = specType->semantic(loc, sc); } #if 0 // Don't do semantic() until instantiation if (defaultType) { defaultType = defaultType->semantic(loc, sc); } #endif } /**************************************** * Determine if two TemplateParameters are the same * as far as TemplateDeclaration overloading goes. * Returns: * 1 match * 0 no match */ int TemplateTypeParameter::overloadMatch(TemplateParameter *tp) { TemplateTypeParameter *ttp = tp->isTemplateTypeParameter(); if (ttp) { if (specType != ttp->specType) goto Lnomatch; if (specType && !specType->equals(ttp->specType)) goto Lnomatch; return 1; // match } Lnomatch: return 0; } /******************************************* * Match to a particular TemplateParameter. * Input: * i i'th argument * tiargs[] actual arguments to template instance * parameters[] template parameters * dedtypes[] deduced arguments to template instance * *psparam set to symbol declared and initialized to dedtypes[i] * flags 1: don't do 'toHeadMutable()' */ MATCH TemplateTypeParameter::matchArg(Scope *sc, Objects *tiargs, int i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam, int flags) { //printf("TemplateTypeParameter::matchArg()\n"); Type *t; Object *oarg; MATCH m = MATCHexact; Type *ta; if (i < tiargs->dim) oarg = (Object *)tiargs->data[i]; else { // Get default argument instead oarg = defaultArg(loc, sc); if (!oarg) { assert(i < dedtypes->dim); // It might have already been deduced oarg = (Object *)dedtypes->data[i]; if (!oarg) { goto Lnomatch; } flags |= 1; // already deduced, so don't to toHeadMutable() } } ta = isType(oarg); if (!ta) { //printf("%s %p %p %p\n", oarg->toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg)); goto Lnomatch; } //printf("ta is %s\n", ta->toChars()); t = (Type *)dedtypes->data[i]; if (specType) { //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta->toChars(), specType->toChars()); MATCH m2 = ta->deduceType(sc, specType, parameters, dedtypes); if (m2 == MATCHnomatch) { //printf("\tfailed deduceType\n"); goto Lnomatch; } if (m2 < m) m = m2; t = (Type *)dedtypes->data[i]; } else { // So that matches with specializations are better m = MATCHconvert; /* This is so that: * template Foo(T), Foo!(const int), => ta == int */ // if (!(flags & 1)) // ta = ta->toHeadMutable(); if (t) { // Must match already deduced type m = MATCHexact; if (!t->equals(ta)) { //printf("t = %s ta = %s\n", t->toChars(), ta->toChars()); goto Lnomatch; } } } if (!t) { dedtypes->data[i] = ta; t = ta; } *psparam = new AliasDeclaration(loc, ident, t); //printf("\tm = %d\n", m); return m; Lnomatch: *psparam = NULL; //printf("\tm = %d\n", MATCHnomatch); return MATCHnomatch; } void TemplateTypeParameter::print(Object *oarg, Object *oded) { printf(" %s\n", ident->toChars()); Type *t = isType(oarg); Type *ta = isType(oded); assert(ta); if (specType) printf("\tSpecialization: %s\n", specType->toChars()); if (defaultType) printf("\tDefault: %s\n", defaultType->toChars()); printf("\tArgument: %s\n", t ? t->toChars() : "NULL"); printf("\tDeduced Type: %s\n", ta->toChars()); } void TemplateTypeParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring(ident->toChars()); if (specType) { buf->writestring(" : "); specType->toCBuffer(buf, NULL, hgs); } if (defaultType) { buf->writestring(" = "); defaultType->toCBuffer(buf, NULL, hgs); } } void *TemplateTypeParameter::dummyArg() { Type *t; if (specType) t = specType; else { // Use this for alias-parameter's too (?) t = new TypeIdentifier(loc, ident); } return (void *)t; } Object *TemplateTypeParameter::specialization() { return specType; } Object *TemplateTypeParameter::defaultArg(Loc loc, Scope *sc) { Type *t; t = defaultType; if (t) { t = t->syntaxCopy(); t = t->semantic(loc, sc); } return t; } /* ======================== TemplateThisParameter =========================== */ #if DMDV2 // this-parameter TemplateThisParameter::TemplateThisParameter(Loc loc, Identifier *ident, Type *specType, Type *defaultType) : TemplateTypeParameter(loc, ident, specType, defaultType) { } TemplateThisParameter *TemplateThisParameter::isTemplateThisParameter() { return this; } TemplateParameter *TemplateThisParameter::syntaxCopy() { TemplateThisParameter *tp = new TemplateThisParameter(loc, ident, specType, defaultType); if (tp->specType) tp->specType = specType->syntaxCopy(); if (defaultType) tp->defaultType = defaultType->syntaxCopy(); return tp; } void TemplateThisParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("this "); TemplateTypeParameter::toCBuffer(buf, hgs); } #endif /* ======================== TemplateAliasParameter ========================== */ // alias-parameter Dsymbol *TemplateAliasParameter::sdummy = NULL; TemplateAliasParameter::TemplateAliasParameter(Loc loc, Identifier *ident, Type *specType, Object *specAlias, Object *defaultAlias) : TemplateParameter(loc, ident) { this->ident = ident; this->specType = specType; this->specAlias = specAlias; this->defaultAlias = defaultAlias; } TemplateAliasParameter *TemplateAliasParameter::isTemplateAliasParameter() { return this; } TemplateParameter *TemplateAliasParameter::syntaxCopy() { TemplateAliasParameter *tp = new TemplateAliasParameter(loc, ident, specType, specAlias, defaultAlias); if (tp->specType) tp->specType = specType->syntaxCopy(); tp->specAlias = objectSyntaxCopy(specAlias); tp->defaultAlias = objectSyntaxCopy(defaultAlias); return tp; } void TemplateAliasParameter::declareParameter(Scope *sc) { TypeIdentifier *ti = new TypeIdentifier(loc, ident); sparam = new AliasDeclaration(loc, ident, ti); if (!sc->insert(sparam)) error(loc, "parameter '%s' multiply defined", ident->toChars()); } Object *aliasParameterSemantic(Loc loc, Scope *sc, Object *o) { if (o) { Expression *ea = isExpression(o); Type *ta = isType(o); if (ta) { Dsymbol *s = ta->toDsymbol(sc); if (s) o = s; else o = ta->semantic(loc, sc); } else if (ea) { ea = ea->semantic(sc); o = ea->optimize(WANTvalue | WANTinterpret); } } return o; } void TemplateAliasParameter::semantic(Scope *sc) { if (specType) { specType = specType->semantic(loc, sc); } specAlias = aliasParameterSemantic(loc, sc, specAlias); #if 0 // Don't do semantic() until instantiation if (defaultAlias) defaultAlias = defaultAlias->semantic(loc, sc); #endif } int TemplateAliasParameter::overloadMatch(TemplateParameter *tp) { TemplateAliasParameter *tap = tp->isTemplateAliasParameter(); if (tap) { if (specAlias != tap->specAlias) goto Lnomatch; return 1; // match } Lnomatch: return 0; } MATCH TemplateAliasParameter::matchArg(Scope *sc, Objects *tiargs, int i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam, int flags) { Object *sa; Object *oarg; Expression *ea; Dsymbol *s; //printf("TemplateAliasParameter::matchArg()\n"); if (i < tiargs->dim) oarg = (Object *)tiargs->data[i]; else { // Get default argument instead oarg = defaultArg(loc, sc); if (!oarg) { assert(i < dedtypes->dim); // It might have already been deduced oarg = (Object *)dedtypes->data[i]; if (!oarg) goto Lnomatch; } } sa = getDsymbol(oarg); if (sa) { /* specType means the alias must be a declaration with a type * that matches specType. */ if (specType) { Declaration *d = ((Dsymbol *)sa)->isDeclaration(); if (!d) goto Lnomatch; if (!d->type->equals(specType)) goto Lnomatch; } } else { sa = oarg; ea = isExpression(oarg); if (ea) { if (specType) { if (!ea->type->equals(specType)) goto Lnomatch; } } else goto Lnomatch; } if (specAlias) { if (sa == sdummy) goto Lnomatch; if (sa != specAlias) goto Lnomatch; } else if (dedtypes->data[i]) { // Must match already deduced symbol Object *s = (Object *)dedtypes->data[i]; if (!sa || s != sa) goto Lnomatch; } dedtypes->data[i] = sa; s = isDsymbol(sa); if (s) *psparam = new AliasDeclaration(loc, ident, s); else { assert(ea); // Declare manifest constant Initializer *init = new ExpInitializer(loc, ea); VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init); v->storage_class = STCmanifest; v->semantic(sc); *psparam = v; } return MATCHexact; Lnomatch: *psparam = NULL; return MATCHnomatch; } void TemplateAliasParameter::print(Object *oarg, Object *oded) { printf(" %s\n", ident->toChars()); Dsymbol *sa = isDsymbol(oded); assert(sa); printf("\tArgument alias: %s\n", sa->toChars()); } void TemplateAliasParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("alias "); if (specType) { HdrGenState hgs; specType->toCBuffer(buf, ident, &hgs); } else buf->writestring(ident->toChars()); if (specAlias) { buf->writestring(" : "); ObjectToCBuffer(buf, hgs, specAlias); } if (defaultAlias) { buf->writestring(" = "); ObjectToCBuffer(buf, hgs, defaultAlias); } } void *TemplateAliasParameter::dummyArg() { Object *s; s = specAlias; if (!s) { if (!sdummy) sdummy = new Dsymbol(); s = sdummy; } return (void*)s; } Object *TemplateAliasParameter::specialization() { return specAlias; } Object *TemplateAliasParameter::defaultArg(Loc loc, Scope *sc) { Object *o = aliasParameterSemantic(loc, sc, defaultAlias); return o; } /* ======================== TemplateValueParameter ========================== */ // value-parameter Expression *TemplateValueParameter::edummy = NULL; TemplateValueParameter::TemplateValueParameter(Loc loc, Identifier *ident, Type *valType, Expression *specValue, Expression *defaultValue) : TemplateParameter(loc, ident) { this->ident = ident; this->valType = valType; this->specValue = specValue; this->defaultValue = defaultValue; } TemplateValueParameter *TemplateValueParameter::isTemplateValueParameter() { return this; } TemplateParameter *TemplateValueParameter::syntaxCopy() { TemplateValueParameter *tp = new TemplateValueParameter(loc, ident, valType, specValue, defaultValue); tp->valType = valType->syntaxCopy(); if (specValue) tp->specValue = specValue->syntaxCopy(); if (defaultValue) tp->defaultValue = defaultValue->syntaxCopy(); return tp; } void TemplateValueParameter::declareParameter(Scope *sc) { VarDeclaration *v = new VarDeclaration(loc, valType, ident, NULL); v->storage_class = STCtemplateparameter; if (!sc->insert(v)) error(loc, "parameter '%s' multiply defined", ident->toChars()); sparam = v; } void TemplateValueParameter::semantic(Scope *sc) { sparam->semantic(sc); valType = valType->semantic(loc, sc); if (!(valType->isintegral() || valType->isfloating() || valType->isString()) && valType->ty != Tident) error(loc, "arithmetic/string type expected for value-parameter, not %s", valType->toChars()); if (specValue) { Expression *e = specValue; e = e->semantic(sc); e = e->implicitCastTo(sc, valType); e = e->optimize(WANTvalue | WANTinterpret); if (e->op == TOKint64 || e->op == TOKfloat64 || e->op == TOKcomplex80 || e->op == TOKnull || e->op == TOKstring) specValue = e; //e->toInteger(); } #if 0 // defer semantic analysis to arg match if (defaultValue) { Expression *e = defaultValue; e = e->semantic(sc); e = e->implicitCastTo(sc, valType); e = e->optimize(WANTvalue | WANTinterpret); if (e->op == TOKint64) defaultValue = e; //e->toInteger(); } #endif } int TemplateValueParameter::overloadMatch(TemplateParameter *tp) { TemplateValueParameter *tvp = tp->isTemplateValueParameter(); if (tvp) { if (valType != tvp->valType) goto Lnomatch; if (valType && !valType->equals(tvp->valType)) goto Lnomatch; if (specValue != tvp->specValue) goto Lnomatch; return 1; // match } Lnomatch: return 0; } MATCH TemplateValueParameter::matchArg(Scope *sc, Objects *tiargs, int i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam, int flags) { //printf("TemplateValueParameter::matchArg()\n"); Initializer *init; Declaration *sparam; MATCH m = MATCHexact; Expression *ei; Object *oarg; if (i < tiargs->dim) oarg = (Object *)tiargs->data[i]; else { // Get default argument instead oarg = defaultArg(loc, sc); if (!oarg) { assert(i < dedtypes->dim); // It might have already been deduced oarg = (Object *)dedtypes->data[i]; if (!oarg) goto Lnomatch; } } ei = isExpression(oarg); Type *vt; if (!ei && oarg) goto Lnomatch; if (ei && ei->op == TOKvar) { // Resolve const variables that we had skipped earlier ei = ei->optimize(WANTvalue | WANTinterpret); } if (specValue) { if (!ei || ei == edummy) goto Lnomatch; Expression *e = specValue; e = e->semantic(sc); e = e->implicitCastTo(sc, valType); e = e->optimize(WANTvalue | WANTinterpret); //e->type = e->type->toHeadMutable(); ei = ei->syntaxCopy(); ei = ei->semantic(sc); ei = ei->optimize(WANTvalue | WANTinterpret); //ei->type = ei->type->toHeadMutable(); //printf("\tei: %s, %s\n", ei->toChars(), ei->type->toChars()); //printf("\te : %s, %s\n", e->toChars(), e->type->toChars()); if (!ei->equals(e)) goto Lnomatch; } else if (dedtypes->data[i]) { // Must match already deduced value Expression *e = (Expression *)dedtypes->data[i]; if (!ei || !ei->equals(e)) goto Lnomatch; } Lmatch: //printf("\tvalType: %s, ty = %d\n", valType->toChars(), valType->ty); vt = valType->semantic(0, sc); //printf("ei: %s, ei->type: %s\n", ei->toChars(), ei->type->toChars()); //printf("vt = %s\n", vt->toChars()); if (ei->type) { //ei->type = ei->type->toHeadMutable(); m = (MATCH)ei->implicitConvTo(vt); //printf("m: %d\n", m); if (!m) goto Lnomatch; } dedtypes->data[i] = ei; init = new ExpInitializer(loc, ei); sparam = new VarDeclaration(loc, vt, ident, init); sparam->storage_class = STCmanifest; *psparam = sparam; return m; Lnomatch: //printf("\tno match\n"); *psparam = NULL; return MATCHnomatch; } void TemplateValueParameter::print(Object *oarg, Object *oded) { printf(" %s\n", ident->toChars()); Expression *ea = isExpression(oded); if (specValue) printf("\tSpecialization: %s\n", specValue->toChars()); printf("\tArgument Value: %s\n", ea ? ea->toChars() : "NULL"); } void TemplateValueParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { valType->toCBuffer(buf, ident, hgs); if (specValue) { buf->writestring(" : "); specValue->toCBuffer(buf, hgs); } if (defaultValue) { buf->writestring(" = "); defaultValue->toCBuffer(buf, hgs); } } void *TemplateValueParameter::dummyArg() { Expression *e; e = specValue; if (!e) { // Create a dummy value if (!edummy) edummy = valType->defaultInit(); e = edummy; } return (void *)e; } Object *TemplateValueParameter::specialization() { return specValue; } Object *TemplateValueParameter::defaultArg(Loc loc, Scope *sc) { Expression *e = defaultValue; if (e) { e = e->syntaxCopy(); e = e->semantic(sc); #if DMDV2 if (e->op == TOKdefault) { DefaultInitExp *de = (DefaultInitExp *)e; e = de->resolve(loc, sc); } #endif } return e; } /* ======================== TemplateTupleParameter ========================== */ // variadic-parameter TemplateTupleParameter::TemplateTupleParameter(Loc loc, Identifier *ident) : TemplateParameter(loc, ident) { this->ident = ident; } TemplateTupleParameter *TemplateTupleParameter::isTemplateTupleParameter() { return this; } TemplateParameter *TemplateTupleParameter::syntaxCopy() { TemplateTupleParameter *tp = new TemplateTupleParameter(loc, ident); return tp; } void TemplateTupleParameter::declareParameter(Scope *sc) { TypeIdentifier *ti = new TypeIdentifier(loc, ident); sparam = new AliasDeclaration(loc, ident, ti); if (!sc->insert(sparam)) error(loc, "parameter '%s' multiply defined", ident->toChars()); } void TemplateTupleParameter::semantic(Scope *sc) { } int TemplateTupleParameter::overloadMatch(TemplateParameter *tp) { TemplateTupleParameter *tvp = tp->isTemplateTupleParameter(); if (tvp) { return 1; // match } Lnomatch: return 0; } MATCH TemplateTupleParameter::matchArg(Scope *sc, Objects *tiargs, int i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam, int flags) { //printf("TemplateTupleParameter::matchArg()\n"); /* The rest of the actual arguments (tiargs[]) form the match * for the variadic parameter. */ assert(i + 1 == dedtypes->dim); // must be the last one Tuple *ovar; if (i + 1 == tiargs->dim && isTuple((Object *)tiargs->data[i])) ovar = isTuple((Object *)tiargs->data[i]); else { ovar = new Tuple(); //printf("ovar = %p\n", ovar); if (i < tiargs->dim) { //printf("i = %d, tiargs->dim = %d\n", i, tiargs->dim); ovar->objects.setDim(tiargs->dim - i); for (size_t j = 0; j < ovar->objects.dim; j++) ovar->objects.data[j] = tiargs->data[i + j]; } } *psparam = new TupleDeclaration(loc, ident, &ovar->objects); dedtypes->data[i] = (void *)ovar; return MATCHexact; } void TemplateTupleParameter::print(Object *oarg, Object *oded) { printf(" %s... [", ident->toChars()); Tuple *v = isTuple(oded); assert(v); //printf("|%d| ", v->objects.dim); for (int i = 0; i < v->objects.dim; i++) { if (i) printf(", "); Object *o = (Object *)v->objects.data[i]; Dsymbol *sa = isDsymbol(o); if (sa) printf("alias: %s", sa->toChars()); Type *ta = isType(o); if (ta) printf("type: %s", ta->toChars()); Expression *ea = isExpression(o); if (ea) printf("exp: %s", ea->toChars()); assert(!isTuple(o)); // no nested Tuple arguments } printf("]\n"); } void TemplateTupleParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring(ident->toChars()); buf->writestring("..."); } void *TemplateTupleParameter::dummyArg() { return NULL; } Object *TemplateTupleParameter::specialization() { return NULL; } Object *TemplateTupleParameter::defaultArg(Loc loc, Scope *sc) { return NULL; } /* ======================== TemplateInstance ================================ */ TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) : ScopeDsymbol(NULL) { #if LOG printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident->toChars() : "null"); #endif this->loc = loc; this->name = ident; this->tiargs = NULL; this->tempdecl = NULL; this->inst = NULL; this->argsym = NULL; this->aliasdecl = NULL; this->semanticdone = 0; this->semantictiargsdone = 0; this->withsym = NULL; this->nest = 0; this->havetempdecl = 0; this->isnested = NULL; this->errors = 0; // LDC this->tinst = NULL; this->tmodule = NULL; } /***************** * This constructor is only called when we figured out which function * template to instantiate. */ TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *tiargs) : ScopeDsymbol(NULL) { #if LOG printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td->toChars()); #endif this->loc = loc; this->name = td->ident; this->tiargs = tiargs; this->tempdecl = td; this->inst = NULL; this->argsym = NULL; this->aliasdecl = NULL; this->semanticdone = 0; this->semantictiargsdone = 1; this->withsym = NULL; this->nest = 0; this->havetempdecl = 1; this->isnested = NULL; this->errors = 0; // LDC this->tinst = NULL; this->tmodule = NULL; assert((size_t)tempdecl->scope > 0x10000); } Objects *TemplateInstance::arraySyntaxCopy(Objects *objs) { Objects *a = NULL; if (objs) { a = new Objects(); a->setDim(objs->dim); for (size_t i = 0; i < objs->dim; i++) { a->data[i] = objectSyntaxCopy((Object *)objs->data[i]); } } return a; } Dsymbol *TemplateInstance::syntaxCopy(Dsymbol *s) { TemplateInstance *ti; if (s) ti = (TemplateInstance *)s; else ti = new TemplateInstance(loc, name); ti->tiargs = arraySyntaxCopy(tiargs); ScopeDsymbol::syntaxCopy(ti); return ti; } void TemplateInstance::semantic(Scope *sc) { if (global.errors) { if (!global.gag) { /* Trying to soldier on rarely generates useful messages * at this point. */ fatal(); } return; } #if LOG printf("\n+TemplateInstance::semantic('%s', this=%p)\n", toChars(), this); #endif if (inst) // if semantic() was already run { #if LOG printf("-TemplateInstance::semantic('%s', this=%p) already run\n", inst->toChars(), inst); #endif return; } if (semanticdone != 0) { error(loc, "recursive template expansion"); // inst = this; return; } semanticdone = 1; // get the enclosing template instance from the scope tinst tinst = sc->tinst; // get the module of the outermost enclosing instantiation if (tinst) tmodule = tinst->tmodule; else tmodule = sc->module; //printf("%s in %s\n", toChars(), tmodule->toChars()); #if LOG printf("\tdo semantic\n"); #endif if (havetempdecl) { assert((size_t)tempdecl->scope > 0x10000); // Deduce tdtypes tdtypes.setDim(tempdecl->parameters->dim); if (!tempdecl->matchWithInstance(this, &tdtypes, 2)) { error("incompatible arguments for template instantiation"); inst = this; return; } } else { /* Run semantic on each argument, place results in tiargs[] * (if we havetempdecl, then tiargs is already evaluated) */ semanticTiargs(sc); tempdecl = findTemplateDeclaration(sc); if (tempdecl) tempdecl = findBestMatch(sc); if (!tempdecl || global.errors) { inst = this; //printf("error return %p, %d\n", tempdecl, global.errors); return; // error recovery } } isNested(tiargs); /* See if there is an existing TemplateInstantiation that already * implements the typeargs. If so, just refer to that one instead. */ for (size_t i = 0; i < tempdecl->instances.dim; i++) { TemplateInstance *ti = (TemplateInstance *)tempdecl->instances.data[i]; #if LOG printf("\t%s: checking for match with instance %d (%p): '%s'\n", toChars(), i, ti, ti->toChars()); #endif assert(tdtypes.dim == ti->tdtypes.dim); // Nesting must match if (isnested != ti->isnested) continue; #if 0 if (isnested && sc->parent != ti->parent) continue; #endif for (size_t j = 0; j < tdtypes.dim; j++) { Object *o1 = (Object *)tdtypes.data[j]; Object *o2 = (Object *)ti->tdtypes.data[j]; if (!match(o1, o2, tempdecl, sc)) goto L1; } // It's a match inst = ti; parent = ti->parent; #if LOG printf("\tit's a match with instance %p\n", inst); #endif return; L1: ; } /* So, we need to implement 'this' instance. */ #if LOG printf("\timplement template instance '%s'\n", toChars()); #endif unsigned errorsave = global.errors; inst = this; int tempdecl_instance_idx = tempdecl->instances.dim; tempdecl->instances.push(this); parent = tempdecl->parent; //printf("parent = '%s'\n", parent->kind()); ident = genIdent(); // need an identifier for name mangling purposes. #if 1 if (isnested) parent = isnested; #endif //printf("parent = '%s'\n", parent->kind()); // Add 'this' to the enclosing scope's members[] so the semantic routines // will get called on the instance members #if 1 int dosemantic3 = 0; { Array *a; Scope *scx = sc; #if 0 for (scx = sc; scx; scx = scx->enclosing) if (scx->scopesym) break; #endif //if (scx && scx->scopesym) printf("3: scx is %s %s\n", scx->scopesym->kind(), scx->scopesym->toChars()); if (scx && scx->scopesym && scx->scopesym->members && !scx->scopesym->isTemplateMixin()) { //printf("\t1: adding to %s %s\n", scx->scopesym->kind(), scx->scopesym->toChars()); a = scx->scopesym->members; } else { Module *m = sc->module->importedFrom; //printf("\t2: adding to module %s instead of module %s\n", m->toChars(), sc->module->toChars()); a = m->members; if (m->semanticdone >= 3) dosemantic3 = 1; } for (int i = 0; 1; i++) { if (i == a->dim) { a->push(this); break; } if (this == (Dsymbol *)a->data[i]) // if already in Array break; } } #endif // Copy the syntax trees from the TemplateDeclaration members = Dsymbol::arraySyntaxCopy(tempdecl->members); // Create our own scope for the template parameters Scope *scope = tempdecl->scope; if (!scope) { error("forward reference to template declaration %s\n", tempdecl->toChars()); return; } #if LOG printf("\tcreate scope for template parameters '%s'\n", toChars()); #endif argsym = new ScopeDsymbol(); argsym->parent = scope->parent; scope = scope->push(argsym); // Declare each template parameter as an alias for the argument type declareParameters(scope); // Add members of template instance to template instance symbol table // parent = scope->scopesym; symtab = new DsymbolTable(); int memnum = 0; for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; #if LOG printf("\t[%d] adding member '%s' %p kind %s to '%s', memnum = %d\n", i, s->toChars(), s, s->kind(), this->toChars(), memnum); #endif memnum |= s->addMember(scope, this, memnum); } #if LOG printf("adding members done\n"); #endif /* See if there is only one member of template instance, and that * member has the same name as the template instance. * If so, this template instance becomes an alias for that member. */ //printf("members->dim = %d\n", members->dim); if (members->dim) { Dsymbol *s; if (Dsymbol::oneMembers(members, &s) && s) { //printf("s->kind = '%s'\n", s->kind()); //s->print(); //printf("'%s', '%s'\n", s->ident->toChars(), tempdecl->ident->toChars()); if (s->ident && s->ident->equals(tempdecl->ident)) { //printf("setting aliasdecl\n"); aliasdecl = new AliasDeclaration(loc, s->ident, s); } } } // Do semantic() analysis on template instance members #if LOG printf("\tdo semantic() on template instance members '%s'\n", toChars()); #endif Scope *sc2; sc2 = scope->push(this); //printf("isnested = %d, sc->parent = %s\n", isnested, sc->parent->toChars()); sc2->parent = /*isnested ? sc->parent :*/ this; sc2->tinst = this; #if !IN_LLVM #if WINDOWS_SEH __try { #endif #endif for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; //printf("\t[%d] semantic on '%s' %p kind %s in '%s'\n", i, s->toChars(), s, s->kind(), this->toChars()); //printf("test: isnested = %d, sc2->parent = %s\n", isnested, sc2->parent->toChars()); // if (isnested) // s->parent = sc->parent; //printf("test3: isnested = %d, s->parent = %s\n", isnested, s->parent->toChars()); s->semantic(sc2); //printf("test4: isnested = %d, s->parent = %s\n", isnested, s->parent->toChars()); sc2->module->runDeferredSemantic(); } #if !IN_LLVM #if WINDOWS_SEH } __except (__ehfilter(GetExceptionInformation())) { global.gag = 0; // ensure error message gets printed error("recursive expansion"); fatal(); } #endif #endif /* If any of the instantiation members didn't get semantic() run * on them due to forward references, we cannot run semantic2() * or semantic3() yet. */ for (size_t i = 0; i < Module::deferred.dim; i++) { Dsymbol *sd = (Dsymbol *)Module::deferred.data[i]; if (sd->parent == this) goto Laftersemantic; } /* The problem is when to parse the initializer for a variable. * Perhaps VarDeclaration::semantic() should do it like it does * for initializers inside a function. */ // if (sc->parent->isFuncDeclaration()) /* BUG 782: this has problems if the classes this depends on * are forward referenced. Find a way to defer semantic() * on this template. */ semantic2(sc2); if (sc->func || dosemantic3) { semantic3(sc2); } Laftersemantic: sc2->pop(); scope->pop(); // Give additional context info if error occurred during instantiation if (global.errors != errorsave) { error("error instantiating"); if(tinst) tinst->printInstantiationTrace(); errors = 1; if (global.gag) tempdecl->instances.remove(tempdecl_instance_idx); } #if LOG printf("-TemplateInstance::semantic('%s', this=%p)\n", toChars(), this); #endif } void TemplateInstance::semanticTiargs(Scope *sc) { //printf("+TemplateInstance::semanticTiargs() %s\n", toChars()); if (semantictiargsdone) return; semantictiargsdone = 1; semanticTiargs(loc, sc, tiargs, 0); } /********************************** * Input: * flags 1: replace const variables with their initializers */ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) { // Run semantic on each argument, place results in tiargs[] //printf("+TemplateInstance::semanticTiargs()\n"); if (!tiargs) return; for (size_t j = 0; j < tiargs->dim; j++) { Object *o = (Object *)tiargs->data[j]; Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); //printf("1: tiargs->data[%d] = %p, %p, %p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta); if (ta) { //printf("type %s\n", ta->toChars()); // It might really be an Expression or an Alias ta->resolve(loc, sc, &ea, &ta, &sa); if (ea) { ea = ea->semantic(sc); /* This test is to skip substituting a const var with * its initializer. The problem is the initializer won't * match with an 'alias' parameter. Instead, do the * const substitution in TemplateValueParameter::matchArg(). */ if (ea->op != TOKvar || flags & 1) ea = ea->optimize(WANTvalue | WANTinterpret); tiargs->data[j] = ea; } else if (sa) { tiargs->data[j] = sa; TupleDeclaration *d = sa->toAlias()->isTupleDeclaration(); if (d) { size_t dim = d->objects->dim; tiargs->remove(j); tiargs->insert(j, d->objects); j--; } } else if (ta) { if (ta->ty == Ttuple) { // Expand tuple TypeTuple *tt = (TypeTuple *)ta; size_t dim = tt->arguments->dim; tiargs->remove(j); if (dim) { tiargs->reserve(dim); for (size_t i = 0; i < dim; i++) { Argument *arg = (Argument *)tt->arguments->data[i]; tiargs->insert(j + i, arg->type); } } j--; } else tiargs->data[j] = ta; } else { assert(global.errors); tiargs->data[j] = Type::terror; } } else if (ea) { if (!ea) { assert(global.errors); ea = new IntegerExp(0); } assert(ea); ea = ea->semantic(sc); if (ea->op != TOKvar || flags & 1) ea = ea->optimize(WANTvalue | WANTinterpret); tiargs->data[j] = ea; if (ea->op == TOKtype) tiargs->data[j] = ea->type; } else if (sa) { } else { assert(0); } //printf("1: tiargs->data[%d] = %p\n", j, tiargs->data[j]); } #if 0 printf("-TemplateInstance::semanticTiargs('%s', this=%p)\n", toChars(), this); for (size_t j = 0; j < tiargs->dim; j++) { Object *o = (Object *)tiargs->data[j]; Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Tuple *va = isTuple(o); printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va); } #endif } /********************************************** * Find template declaration corresponding to template instance. */ TemplateDeclaration *TemplateInstance::findTemplateDeclaration(Scope *sc) { //printf("TemplateInstance::findTemplateDeclaration() %s\n", toChars()); if (!tempdecl) { /* Given: * foo!( ... ) * figure out which TemplateDeclaration foo refers to. */ Dsymbol *s; Dsymbol *scopesym; Identifier *id; int i; id = name; s = sc->search(loc, id, &scopesym); if (!s) { error("identifier '%s' is not defined", id->toChars()); return NULL; } #if LOG printf("It's an instance of '%s' kind '%s'\n", s->toChars(), s->kind()); if (s->parent) printf("s->parent = '%s'\n", s->parent->toChars()); #endif withsym = scopesym->isWithScopeSymbol(); /* We might have found an alias within a template when * we really want the template. */ TemplateInstance *ti; if (s->parent && (ti = s->parent->isTemplateInstance()) != NULL) { if ( (ti->name == id || ti->toAlias()->ident == id) && ti->tempdecl) { /* This is so that one can refer to the enclosing * template, even if it has the same name as a member * of the template, if it has a !(arguments) */ tempdecl = ti->tempdecl; if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's tempdecl = tempdecl->overroot; // then get the start s = tempdecl; } } s = s->toAlias(); /* It should be a TemplateDeclaration, not some other symbol */ tempdecl = s->isTemplateDeclaration(); if (!tempdecl) { if (!s->parent && global.errors) return NULL; if (!s->parent && s->getType()) { Dsymbol *s2 = s->getType()->toDsymbol(sc); if (!s2) { error("%s is not a template declaration, it is a %s", id->toChars(), s->kind()); return NULL; } s = s2; } #ifdef DEBUG //if (!s->parent) printf("s = %s %s\n", s->kind(), s->toChars()); #endif //assert(s->parent); TemplateInstance *ti = s->parent ? s->parent->isTemplateInstance() : NULL; if (ti && (ti->name == id || ti->toAlias()->ident == id) && ti->tempdecl) { /* This is so that one can refer to the enclosing * template, even if it has the same name as a member * of the template, if it has a !(arguments) */ tempdecl = ti->tempdecl; if (tempdecl->overroot) // if not start of overloaded list of TemplateDeclaration's tempdecl = tempdecl->overroot; // then get the start } else { error("%s is not a template declaration, it is a %s", id->toChars(), s->kind()); return NULL; } } } else assert(tempdecl->isTemplateDeclaration()); return tempdecl; } TemplateDeclaration *TemplateInstance::findBestMatch(Scope *sc) { /* Since there can be multiple TemplateDeclaration's with the same * name, look for the best match. */ TemplateDeclaration *td_ambig = NULL; TemplateDeclaration *td_best = NULL; MATCH m_best = MATCHnomatch; Objects dedtypes; #if LOG printf("TemplateInstance::findBestMatch()\n"); #endif for (TemplateDeclaration *td = tempdecl; td; td = td->overnext) { MATCH m; //if (tiargs->dim) printf("2: tiargs->dim = %d, data[0] = %p\n", tiargs->dim, tiargs->data[0]); // If more arguments than parameters, // then this is no match. if (td->parameters->dim < tiargs->dim) { if (!td->isVariadic()) continue; } dedtypes.setDim(td->parameters->dim); dedtypes.zero(); if (!td->scope) { error("forward reference to template declaration %s", td->toChars()); return NULL; } m = td->matchWithInstance(this, &dedtypes, 0); //printf("matchWithInstance = %d\n", m); if (!m) // no match at all continue; if (m < m_best) goto Ltd_best; if (m > m_best) goto Ltd; { // Disambiguate by picking the most specialized TemplateDeclaration MATCH c1 = td->leastAsSpecialized(td_best); MATCH c2 = td_best->leastAsSpecialized(td); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; else if (c1 < c2) goto Ltd_best; else goto Lambig; } Lambig: // td_best and td are ambiguous td_ambig = td; continue; Ltd_best: // td_best is the best match so far td_ambig = NULL; continue; Ltd: // td is the new best match td_ambig = NULL; td_best = td; m_best = m; tdtypes.setDim(dedtypes.dim); memcpy(tdtypes.data, dedtypes.data, tdtypes.dim * sizeof(void *)); continue; } if (!td_best) { if (tempdecl && !tempdecl->overnext) // Only one template, so we can give better error message error("%s does not match template declaration %s", toChars(), tempdecl->toChars()); else error("%s does not match any template declaration", toChars()); return NULL; } if (td_ambig) { error("%s matches more than one template declaration, %s and %s", toChars(), td_best->toChars(), td_ambig->toChars()); } /* The best match is td_best */ tempdecl = td_best; #if 0 /* Cast any value arguments to be same type as value parameter */ for (size_t i = 0; i < tiargs->dim; i++) { Object *o = (Object *)tiargs->data[i]; Expression *ea = isExpression(o); // value argument TemplateParameter *tp = (TemplateParameter *)tempdecl->parameters->data[i]; assert(tp); TemplateValueParameter *tvp = tp->isTemplateValueParameter(); if (tvp) { assert(ea); ea = ea->castTo(tvp->valType); ea = ea->optimize(WANTvalue | WANTinterpret); tiargs->data[i] = (Object *)ea; } } #endif #if LOG printf("\tIt's a match with template declaration '%s'\n", tempdecl->toChars()); #endif return tempdecl; } /***************************************** * Determines if a TemplateInstance will need a nested * generation of the TemplateDeclaration. */ int TemplateInstance::isNested(Objects *args) { int nested = 0; //printf("TemplateInstance::isNested('%s')\n", tempdecl->ident->toChars()); /* A nested instance happens when an argument references a local * symbol that is on the stack. */ for (size_t i = 0; i < args->dim; i++) { Object *o = (Object *)args->data[i]; Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Tuple *va = isTuple(o); if (ea) { if (ea->op == TOKvar) { sa = ((VarExp *)ea)->var; goto Lsa; } if (ea->op == TOKfunction) { sa = ((FuncExp *)ea)->fd; goto Lsa; } } else if (sa) { Lsa: Declaration *d = sa->isDeclaration(); if (d && !d->isDataseg() && #if DMDV2 !(d->storage_class & STCmanifest) && #endif (!d->isFuncDeclaration() || d->isFuncDeclaration()->isNested()) && !isTemplateMixin()) { // if module level template if (tempdecl->toParent()->isModule()) { Dsymbol *dparent = d->toParent(); if (!isnested) isnested = dparent; else if (isnested != dparent) { /* Select the more deeply nested of the two. * Error if one is not nested inside the other. */ for (Dsymbol *p = isnested; p; p = p->parent) { if (p == dparent) goto L1; // isnested is most nested } for (Dsymbol *p = dparent; 1; p = p->parent) { if (p == isnested) { isnested = dparent; goto L1; // dparent is most nested } } error("is nested in both %s and %s", isnested->toChars(), dparent->toChars()); } L1: //printf("\tnested inside %s\n", isnested->toChars()); nested |= 1; } else error("cannot use local '%s' as parameter to non-global template %s", d->toChars(), tempdecl->toChars()); } } else if (va) { nested |= isNested(&va->objects); } } return nested; } /**************************************** * This instance needs an identifier for name mangling purposes. * Create one by taking the template declaration name and adding * the type signature for it. */ Identifier *TemplateInstance::genIdent() { OutBuffer buf; char *id; Objects *args; //printf("TemplateInstance::genIdent('%s')\n", tempdecl->ident->toChars()); id = tempdecl->ident->toChars(); buf.printf("__T%"PRIuSIZE"%s", strlen(id), id); args = tiargs; for (int i = 0; i < args->dim; i++) { Object *o = (Object *)args->data[i]; Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Tuple *va = isTuple(o); //printf("\to %p ta %p ea %p sa %p va %p\n", o, ta, ea, sa, va); if (ta) { buf.writeByte('T'); if (ta->deco) buf.writestring(ta->deco); else { #ifdef DEBUG printf("ta = %d, %s\n", ta->ty, ta->toChars()); #endif assert(global.errors); } } else if (ea) { Lea: sinteger_t v; real_t r; ea = ea->optimize(WANTvalue | WANTinterpret); if (ea->op == TOKvar) { sa = ((VarExp *)ea)->var; ea = NULL; goto Lsa; } if (ea->op == TOKfunction) { sa = ((FuncExp *)ea)->fd; ea = NULL; goto Lsa; } buf.writeByte('V'); if (ea->op == TOKtuple) { ea->error("tuple is not a valid template value argument"); continue; } #if 1 /* Use deco that matches what it would be for a function parameter */ //buf.writestring(ea->type->toHeadMutable()->deco); buf.writestring(ea->type->deco); #else // Use type of parameter, not type of argument TemplateParameter *tp = (TemplateParameter *)tempdecl->parameters->data[i]; assert(tp); TemplateValueParameter *tvp = tp->isTemplateValueParameter(); assert(tvp); buf.writestring(tvp->valType->deco); #endif ea->toMangleBuffer(&buf); } else if (sa) { Lsa: buf.writeByte('S'); Declaration *d = sa->isDeclaration(); if (d && !d->type->deco) { error("forward reference of %s", d->toChars()); continue; } #if 0 VarDeclaration *v = sa->isVarDeclaration(); if (v && v->storage_class & STCmanifest) { ExpInitializer *ei = v->init->isExpInitializer(); if (ei) { ea = ei->exp; goto Lea; } } #endif const char *p = sa->mangle(); buf.printf("%zu%s", strlen(p), p); } else if (va) { assert(i + 1 == args->dim); // must be last one args = &va->objects; i = -1; } else assert(0); } buf.writeByte('Z'); id = buf.toChars(); buf.data = NULL; //printf("\tgenIdent = %s\n", id); return new Identifier(id, TOKidentifier); } /**************************************************** * Declare parameters of template instance, initialize them with the * template instance arguments. */ void TemplateInstance::declareParameters(Scope *scope) { //printf("TemplateInstance::declareParameters()\n"); for (int i = 0; i < tdtypes.dim; i++) { TemplateParameter *tp = (TemplateParameter *)tempdecl->parameters->data[i]; //Object *o = (Object *)tiargs->data[i]; Object *o = (Object *)tdtypes.data[i]; // initializer for tp //printf("\ttdtypes[%d] = %p\n", i, o); tempdecl->declareParameter(scope, tp, o); } } void TemplateInstance::semantic2(Scope *sc) { int i; if (semanticdone >= 2) return; semanticdone = 2; #if LOG printf("+TemplateInstance::semantic2('%s')\n", toChars()); #endif if (!errors && members) { sc = tempdecl->scope; assert(sc); sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; for (i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; #if LOG printf("\tmember '%s', kind = '%s'\n", s->toChars(), s->kind()); #endif s->semantic2(sc); } sc = sc->pop(); sc->pop(); } #if LOG printf("-TemplateInstance::semantic2('%s')\n", toChars()); #endif } void TemplateInstance::semantic3(Scope *sc) { #if LOG printf("TemplateInstance::semantic3('%s'), semanticdone = %d\n", toChars(), semanticdone); #endif //if (toChars()[0] == 'D') *(char*)0=0; if (semanticdone >= 3) return; semanticdone = 3; if (!errors && members) { sc = tempdecl->scope; sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->semantic3(sc); } sc = sc->pop(); sc->pop(); } } void TemplateInstance::toObjFile(int multiobj) { #if LOG printf("TemplateInstance::toObjFile('%s', this = %p)\n", toChars(), this); #endif if (!errors && members) { if (multiobj) // Append to list of object files to be written later //obj_append(this); assert(0 && "multiobj"); else { for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->toObjFile(multiobj); } } } } void TemplateInstance::inlineScan() { #if LOG printf("TemplateInstance::inlineScan('%s')\n", toChars()); #endif if (!errors && members) { for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->inlineScan(); } } } void TemplateInstance::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { int i; Identifier *id = name; buf->writestring(id->toChars()); buf->writestring("!("); if (nest) buf->writestring("..."); else { nest++; Objects *args = tiargs; for (i = 0; i < args->dim; i++) { if (i) buf->writeByte(','); Object *oarg = (Object *)args->data[i]; ObjectToCBuffer(buf, hgs, oarg); } nest--; } buf->writeByte(')'); } Dsymbol *TemplateInstance::toAlias() { #if LOG printf("TemplateInstance::toAlias()\n"); #endif if (!inst) { error("cannot resolve forward reference"); return this; } if (inst != this) return inst->toAlias(); if (aliasdecl) return aliasdecl->toAlias(); return inst; } AliasDeclaration *TemplateInstance::isAliasDeclaration() { return aliasdecl; } const char *TemplateInstance::kind() { return "template instance"; } int TemplateInstance::oneMember(Dsymbol **ps) { *ps = NULL; return TRUE; } char *TemplateInstance::toChars() { OutBuffer buf; HdrGenState hgs; char *s; toCBuffer(&buf, &hgs); s = buf.toChars(); buf.data = NULL; return s; } void TemplateInstance::printInstantiationTrace() { if(global.gag) return; const int max_shown = 6; // determine instantiation depth int n_instantiations = 1; TemplateInstance* cur = this; while(cur = cur->tinst) ++n_instantiations; // show full trace only if it's short or verbose is on if(n_instantiations <= max_shown || global.params.verbose) { cur = this; while(cur) { fprintf(stdmsg," instantiatied in %s: %s\n", cur->loc.toChars(), cur->toChars()); cur = cur->tinst; } } else { cur = this; size_t i = 0; for(; i < max_shown/2; ++i, cur = cur->tinst) fprintf(stdmsg," instantiatied in %s: %s\n", cur->loc.toChars(), cur->toChars()); fprintf(stdmsg," ... (%d instantiations, -v to show) ...\n", n_instantiations - max_shown); for(; i < n_instantiations - max_shown + max_shown/2; ++i, cur = cur->tinst) {} for(; i < n_instantiations; ++i, cur = cur->tinst) fprintf(stdmsg," instantiatied in %s: %s\n", cur->loc.toChars(), cur->toChars()); } } /* ======================== TemplateMixin ================================ */ TemplateMixin::TemplateMixin(Loc loc, Identifier *ident, Type *tqual, Array *idents, Objects *tiargs) : TemplateInstance(loc, (Identifier *)idents->data[idents->dim - 1]) { //printf("TemplateMixin(ident = '%s')\n", ident ? ident->toChars() : ""); this->ident = ident; this->tqual = tqual; this->idents = idents; this->tiargs = tiargs ? tiargs : new Objects(); this->scope = NULL; } Dsymbol *TemplateMixin::syntaxCopy(Dsymbol *s) { TemplateMixin *tm; Array *ids = new Array(); ids->setDim(idents->dim); for (int i = 0; i < idents->dim; i++) { // Matches TypeQualified::syntaxCopyHelper() Identifier *id = (Identifier *)idents->data[i]; if (id->dyncast() == DYNCAST_DSYMBOL) { TemplateInstance *ti = (TemplateInstance *)id; ti = (TemplateInstance *)ti->syntaxCopy(NULL); id = (Identifier *)ti; } ids->data[i] = id; } tm = new TemplateMixin(loc, ident, (Type *)(tqual ? tqual->syntaxCopy() : NULL), ids, tiargs); TemplateInstance::syntaxCopy(tm); return tm; } void TemplateMixin::semantic(Scope *sc) { #if LOG printf("+TemplateMixin::semantic('%s', this=%p)\n", toChars(), this); fflush(stdout); #endif if (semanticdone && // This for when a class/struct contains mixin members, and // is done over because of forward references (!parent || !toParent()->isAggregateDeclaration())) { #if LOG printf("\tsemantic done\n"); #endif return; } if (!semanticdone) semanticdone = 1; #if LOG printf("\tdo semantic\n"); #endif #if !IN_LLVM // dont know what this is util_progress(); #endif Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } // Follow qualifications to find the TemplateDeclaration if (!tempdecl) { Dsymbol *s; int i; Identifier *id; if (tqual) { s = tqual->toDsymbol(sc); i = 0; } else { i = 1; id = (Identifier *)idents->data[0]; switch (id->dyncast()) { case DYNCAST_IDENTIFIER: s = sc->search(loc, id, NULL); break; case DYNCAST_DSYMBOL: { TemplateInstance *ti = (TemplateInstance *)id; ti->semantic(sc); s = ti; break; } default: assert(0); } } for (; i < idents->dim; i++) { if (!s) break; id = (Identifier *)idents->data[i]; s = s->searchX(loc, sc, id); } if (!s) { error("is not defined"); inst = this; return; } tempdecl = s->toAlias()->isTemplateDeclaration(); if (!tempdecl) { error("%s isn't a template", s->toChars()); inst = this; return; } } // Look for forward reference assert(tempdecl); for (TemplateDeclaration *td = tempdecl; td; td = td->overnext) { if (!td->scope) { /* Cannot handle forward references if mixin is a struct member, * because addField must happen during struct's semantic, not * during the mixin semantic. * runDeferred will re-run mixin's semantic outside of the struct's * semantic. */ semanticdone = 0; AggregateDeclaration *ad = toParent()->isAggregateDeclaration(); if (ad) ad->sizeok = 2; else { // Forward reference //printf("forward reference - deferring\n"); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); } return; } } // Run semantic on each argument, place results in tiargs[] semanticTiargs(sc); tempdecl = findBestMatch(sc); if (!tempdecl) { inst = this; return; // error recovery } if (!ident) ident = genIdent(); inst = this; parent = sc->parent; /* Detect recursive mixin instantiations. */ for (Dsymbol *s = parent; s; s = s->parent) { //printf("\ts = '%s'\n", s->toChars()); TemplateMixin *tm = s->isTemplateMixin(); if (!tm || tempdecl != tm->tempdecl) continue; /* Different argument list lengths happen with variadic args */ if (tiargs->dim != tm->tiargs->dim) continue; for (int i = 0; i < tiargs->dim; i++) { Object *o = (Object *)tiargs->data[i]; Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Object *tmo = (Object *)tm->tiargs->data[i]; if (ta) { Type *tmta = isType(tmo); if (!tmta) goto Lcontinue; if (!ta->equals(tmta)) goto Lcontinue; } else if (ea) { Expression *tme = isExpression(tmo); if (!tme || !ea->equals(tme)) goto Lcontinue; } else if (sa) { Dsymbol *tmsa = isDsymbol(tmo); if (sa != tmsa) goto Lcontinue; } else assert(0); } error("recursive mixin instantiation"); return; Lcontinue: continue; } // Copy the syntax trees from the TemplateDeclaration members = Dsymbol::arraySyntaxCopy(tempdecl->members); if (!members) return; symtab = new DsymbolTable(); for (Scope *sce = sc; 1; sce = sce->enclosing) { ScopeDsymbol *sds = (ScopeDsymbol *)sce->scopesym; if (sds) { sds->importScope(this, PROTpublic); break; } } #if LOG printf("\tcreate scope for template parameters '%s'\n", toChars()); #endif Scope *scy = sc; scy = sc->push(this); scy->parent = this; argsym = new ScopeDsymbol(); argsym->parent = scy->parent; Scope *scope = scy->push(argsym); unsigned errorsave = global.errors; // Declare each template parameter as an alias for the argument type declareParameters(scope); // Add members to enclosing scope, as well as this scope for (unsigned i = 0; i < members->dim; i++) { Dsymbol *s; s = (Dsymbol *)members->data[i]; s->addMember(scope, this, i); //sc->insert(s); //printf("sc->parent = %p, sc->scopesym = %p\n", sc->parent, sc->scopesym); //printf("s->parent = %s\n", s->parent->toChars()); } // Do semantic() analysis on template instance members #if LOG printf("\tdo semantic() on template instance members '%s'\n", toChars()); #endif Scope *sc2; sc2 = scope->push(this); sc2->offset = sc->offset; for (int i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->semantic(sc2); } sc->offset = sc2->offset; /* The problem is when to parse the initializer for a variable. * Perhaps VarDeclaration::semantic() should do it like it does * for initializers inside a function. */ // if (sc->parent->isFuncDeclaration()) semantic2(sc2); if (sc->func) { semantic3(sc2); } // Give additional context info if error occurred during instantiation if (global.errors != errorsave) { error("error instantiating"); } sc2->pop(); scope->pop(); // if (!isAnonymous()) { scy->pop(); } #if LOG printf("-TemplateMixin::semantic('%s', this=%p)\n", toChars(), this); #endif } void TemplateMixin::semantic2(Scope *sc) { int i; if (semanticdone >= 2) return; semanticdone = 2; #if LOG printf("+TemplateMixin::semantic2('%s')\n", toChars()); #endif if (members) { assert(sc); sc = sc->push(argsym); sc = sc->push(this); for (i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; #if LOG printf("\tmember '%s', kind = '%s'\n", s->toChars(), s->kind()); #endif s->semantic2(sc); } sc = sc->pop(); sc->pop(); } #if LOG printf("-TemplateMixin::semantic2('%s')\n", toChars()); #endif } void TemplateMixin::semantic3(Scope *sc) { int i; if (semanticdone >= 3) return; semanticdone = 3; #if LOG printf("TemplateMixin::semantic3('%s')\n", toChars()); #endif if (members) { sc = sc->push(argsym); sc = sc->push(this); for (i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; s->semantic3(sc); } sc = sc->pop(); sc->pop(); } } void TemplateMixin::inlineScan() { TemplateInstance::inlineScan(); } const char *TemplateMixin::kind() { return "mixin"; } int TemplateMixin::oneMember(Dsymbol **ps) { return Dsymbol::oneMember(ps); } int TemplateMixin::hasPointers() { //printf("TemplateMixin::hasPointers() %s\n", toChars()); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (Dsymbol *)members->data[i]; //printf(" s = %s %s\n", s->kind(), s->toChars()); if (s->hasPointers()) { return 1; } } return 0; } char *TemplateMixin::toChars() { OutBuffer buf; HdrGenState hgs; char *s; TemplateInstance::toCBuffer(&buf, &hgs); s = buf.toChars(); buf.data = NULL; return s; } void TemplateMixin::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("mixin "); for (int i = 0; i < idents->dim; i++) { Identifier *id = (Identifier *)idents->data[i]; if (i) buf->writeByte('.'); buf->writestring(id->toChars()); } buf->writestring("!("); if (tiargs) { for (int i = 0; i < tiargs->dim; i++) { if (i) buf->writebyte(','); Object *oarg = (Object *)tiargs->data[i]; Type *t = isType(oarg); Expression *e = isExpression(oarg); Dsymbol *s = isDsymbol(oarg); if (t) t->toCBuffer(buf, NULL, hgs); else if (e) e->toCBuffer(buf, hgs); else if (s) { char *p = s->ident ? s->ident->toChars() : s->toChars(); buf->writestring(p); } else if (!oarg) { buf->writestring("NULL"); } else { assert(0); } } } buf->writebyte(')'); if (ident) { buf->writebyte(' '); buf->writestring(ident->toChars()); } buf->writebyte(';'); buf->writenl(); } void TemplateMixin::toObjFile(int multiobj) { //printf("TemplateMixin::toObjFile('%s')\n", toChars()); TemplateInstance::toObjFile(multiobj); }