Mercurial > projects > ldc
view dmd2/template.c @ 1010:bad2044a1597
Automated merge with http://hg.dsource.org/projects/ldc
author | Frits van Bommel <fvbommel wxs.nl> |
---|---|
date | Sat, 28 Feb 2009 17:35:45 +0100 |
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); }