/* Compiler implementation of the D programming language * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/D-Programming-Language/dmd/blob/master/src/template.c */ // Handle template implementation #include "root/dsystem.h" #include "root/root.h" #include "root/aav.h" #include "root/rmem.h" #include "root/stringtable.h" #include "root/hash.h" #include "mangle.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 "mars.h" #include "dsymbol.h" #include "identifier.h" #include "hdrgen.h" #include "id.h" #include "attrib.h" #include "cond.h" #include "tokens.h" #define IDX_NOTFOUND (0x12345678) // index is not found Type *rawTypeMerge(Type *t1, Type *t2); bool MODimplicitConv(MOD modfrom, MOD modto); MATCH MODmethodConv(MOD modfrom, MOD modto); MOD MODmerge(MOD mod1, MOD mod2); static size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters); static int arrayObjectMatch(Objects *oa1, Objects *oa2); static unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam); static MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam); static bool reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0); Expression *semantic(Expression *e, Scope *sc); bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors); /******************************************** * These functions substitute for dynamic_cast. dynamic_cast does not work * on earlier versions of gcc. */ Expression *isExpression(RootObject *o) { //return dynamic_cast(o); if (!o || o->dyncast() != DYNCAST_EXPRESSION) return NULL; return (Expression *)o; } Dsymbol *isDsymbol(RootObject *o) { //return dynamic_cast(o); if (!o || o->dyncast() != DYNCAST_DSYMBOL) return NULL; return (Dsymbol *)o; } Type *isType(RootObject *o) { //return dynamic_cast(o); if (!o || o->dyncast() != DYNCAST_TYPE) return NULL; return (Type *)o; } Tuple *isTuple(RootObject *o) { //return dynamic_cast(o); if (!o || o->dyncast() != DYNCAST_TUPLE) return NULL; return (Tuple *)o; } Parameter *isParameter(RootObject *o) { //return dynamic_cast(o); if (!o || o->dyncast() != DYNCAST_PARAMETER) return NULL; return (Parameter *)o; } /************************************** * Is this Object an error? */ bool isError(RootObject *o) { Type *t = isType(o); if (t) return (t->ty == Terror); Expression *e = isExpression(o); if (e) return (e->op == TOKerror || !e->type || e->type->ty == Terror); Tuple *v = isTuple(o); if (v) return arrayObjectIsError(&v->objects); Dsymbol *s = isDsymbol(o); assert(s); if (s->errors) return true; return s->parent ? isError(s->parent) : false; } /************************************** * Are any of the Objects an error? */ bool arrayObjectIsError(Objects *args) { for (size_t i = 0; i < args->dim; i++) { RootObject *o = (*args)[i]; if (isError(o)) return true; } return false; } /*********************** * Try to get arg as a type. */ Type *getType(RootObject *o) { Type *t = isType(o); if (!t) { Expression *e = isExpression(o); if (e) t = e->type; } return t; } Dsymbol *getDsymbol(RootObject *oarg) { //printf("getDsymbol()\n"); //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(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) { if (((FuncExp *)ea)->td) sa = ((FuncExp *)ea)->td; else sa = ((FuncExp *)ea)->fd; } else if (ea->op == TOKtemplate) sa = ((TemplateExp *)ea)->td; 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; } /*********************** * Try to get value from manifest constant */ static Expression *getValue(Expression *e) { if (e && e->op == TOKvar) { VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); if (v && v->storage_class & STCmanifest) { e = v->getConstInitializer(); } } return e; } static Expression *getValue(Dsymbol *&s) { Expression *e = NULL; if (s) { VarDeclaration *v = s->isVarDeclaration(); if (v && v->storage_class & STCmanifest) { e = v->getConstInitializer(); } } return e; } /********************************** * Return true if e could be valid only as a template value parameter. * Return false if it might be an alias or tuple. * (Note that even in this case, it could still turn out to be a value). */ bool definitelyValueParameter(Expression *e) { // None of these can be value parameters if (e->op == TOKtuple || e->op == TOKscope || e->op == TOKtype || e->op == TOKdottype || e->op == TOKtemplate || e->op == TOKdottd || e->op == TOKfunction || e->op == TOKerror || e->op == TOKthis || e->op == TOKsuper) return false; if (e->op != TOKdotvar) return true; /* Template instantiations involving a DotVar expression are difficult. * In most cases, they should be treated as a value parameter, and interpreted. * But they might also just be a fully qualified name, which should be treated * as an alias. */ // x.y.f cannot be a value FuncDeclaration *f = ((DotVarExp *)e)->var->isFuncDeclaration(); if (f) return false; while (e->op == TOKdotvar) { e = ((DotVarExp *)e)->e1; } // this.x.y and super.x.y couldn't possibly be valid values. if (e->op == TOKthis || e->op == TOKsuper) return false; // e.type.x could be an alias if (e->op == TOKdottype) return false; // var.x.y is the only other possible form of alias if (e->op != TOKvar) return true; VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); // func.x.y is not an alias if (!v) return true; // TODO: Should we force CTFE if it is a global constant? return false; } static Expression *getExpression(RootObject *o) { Dsymbol *s = isDsymbol(o); return s ? getValue(s) : getValue(isExpression(o)); } /****************************** * If o1 matches o2, return true. * Else, return false. */ static bool match(RootObject *o1, RootObject *o2) { //printf("match() o1 = %p %s (%d), o2 = %p %s (%d)\n", // o1, o1->toChars(), o1->dyncast(), o2, o2->toChars(), o2->dyncast()); /* 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. */ /* Manifest constants should be compared by their values, * at least in template arguments. */ if (Type *t1 = isType(o1)) { Type *t2 = isType(o2); if (!t2) goto Lnomatch; //printf("\tt1 = %s\n", t1->toChars()); //printf("\tt2 = %s\n", t2->toChars()); if (!t1->equals(t2)) goto Lnomatch; goto Lmatch; } if (Expression *e1 = getExpression(o1)) { Expression *e2 = getExpression(o2); if (!e2) goto Lnomatch; //printf("\te1 = %s %s %s\n", e1->type->toChars(), Token::toChars(e1->op), e1->toChars()); //printf("\te2 = %s %s %s\n", e2->type->toChars(), Token::toChars(e2->op), e2->toChars()); // two expressions can be equal although they do not have the same // type; that happens when they have the same value. So check type // as well as expression equality to ensure templates are properly // matched. if (!e1->type->equals(e2->type) || !e1->equals(e2)) goto Lnomatch; goto Lmatch; } if (Dsymbol *s1 = isDsymbol(o1)) { Dsymbol *s2 = isDsymbol(o2); if (!s2) goto Lnomatch; //printf("\ts1 = %s\n", s1->toChars()); //printf("\ts2 = %s\n", s2->toChars()); if (!s1->equals(s2)) goto Lnomatch; if (s1->parent != s2->parent && !s1->isFuncDeclaration() && !s2->isFuncDeclaration()) goto Lnomatch; goto Lmatch; } if (Tuple *u1 = isTuple(o1)) { Tuple *u2 = isTuple(o2); if (!u2) goto Lnomatch; //printf("\tu1 = %s\n", u1->toChars()); //printf("\tu2 = %s\n", u2->toChars()); if (!arrayObjectMatch(&u1->objects, &u2->objects)) goto Lnomatch; goto Lmatch; } Lmatch: //printf("\t-> match\n"); return true; Lnomatch: //printf("\t-> nomatch\n"); return false; } /************************************ * Match an array of them. */ int arrayObjectMatch(Objects *oa1, Objects *oa2) { if (oa1 == oa2) return 1; if (oa1->dim != oa2->dim) return 0; for (size_t j = 0; j < oa1->dim; j++) { RootObject *o1 = (*oa1)[j]; RootObject *o2 = (*oa2)[j]; if (!match(o1, o2)) { return 0; } } return 1; } /************************************ * Computes hash of expression. * Handles all Expression classes and MUST match their equals method, * i.e. e1->equals(e2) implies expressionHash(e1) == expressionHash(e2). */ static hash_t expressionHash(Expression *e) { switch (e->op) { case TOKint64: return (size_t) ((IntegerExp *)e)->getInteger(); case TOKfloat64: return CTFloat::hash(((RealExp *)e)->value); case TOKcomplex80: { ComplexExp *ce = (ComplexExp *)e; return mixHash(CTFloat::hash(ce->toReal()), CTFloat::hash(ce->toImaginary())); } case TOKidentifier: return (size_t)(void *) ((IdentifierExp *)e)->ident; case TOKnull: return (size_t)(void *) ((NullExp *)e)->type; case TOKstring: { StringExp *se = (StringExp *)e; return calcHash((const char *)se->string, se->len * se->sz); } case TOKtuple: { TupleExp *te = (TupleExp *)e; size_t hash = 0; hash += te->e0 ? expressionHash(te->e0) : 0; for (size_t i = 0; i < te->exps->dim; i++) { Expression *elem = (*te->exps)[i]; hash = mixHash(hash, expressionHash(elem)); } return hash; } case TOKarrayliteral: { ArrayLiteralExp *ae = (ArrayLiteralExp *)e; size_t hash = 0; for (size_t i = 0; i < ae->elements->dim; i++) hash = mixHash(hash, expressionHash(ae->getElement(i))); return hash; } case TOKassocarrayliteral: { AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e; size_t hash = 0; for (size_t i = 0; i < ae->keys->dim; i++) // reduction needs associative op as keys are unsorted (use XOR) hash ^= mixHash(expressionHash((*ae->keys)[i]), expressionHash((*ae->values)[i])); return hash; } case TOKstructliteral: { StructLiteralExp *se = (StructLiteralExp *)e; size_t hash = 0; for (size_t i = 0; i < se->elements->dim; i++) { Expression *elem = (*se->elements)[i]; hash = mixHash(hash, elem ? expressionHash(elem) : 0); } return hash; } case TOKvar: return (size_t)(void *) ((VarExp *)e)->var; case TOKfunction: return (size_t)(void *) ((FuncExp *)e)->fd; default: // no custom equals for this expression // equals based on identity return (size_t)(void *) e; } } /************************************ * Return hash of Objects. */ static hash_t arrayObjectHash(Objects *oa1) { hash_t hash = 0; for (size_t j = 0; j < oa1->dim; j++) { /* Must follow the logic of match() */ RootObject *o1 = (*oa1)[j]; if (Type *t1 = isType(o1)) hash = mixHash(hash, (size_t)t1->deco); else if (Expression *e1 = getExpression(o1)) hash = mixHash(hash, expressionHash(e1)); else if (Dsymbol *s1 = isDsymbol(o1)) { FuncAliasDeclaration *fa1 = s1->isFuncAliasDeclaration(); if (fa1) s1 = fa1->toAliasFunc(); hash = mixHash(hash, mixHash((size_t)(void *)s1->getIdent(), (size_t)(void *)s1->parent)); } else if (Tuple *u1 = isTuple(o1)) hash = mixHash(hash, arrayObjectHash(&u1->objects)); } return hash; } RootObject *objectSyntaxCopy(RootObject *o) { if (!o) return NULL; if (Type *t = isType(o)) return t->syntaxCopy(); if (Expression *e = isExpression(o)) return e->syntaxCopy(); return o; } /* ======================== TemplateDeclaration ============================= */ TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id, TemplateParameters *parameters, Expression *constraint, Dsymbols *decldefs, bool ismixin, bool literal) : ScopeDsymbol(id) { this->loc = loc; this->parameters = parameters; this->origParameters = parameters; this->constraint = constraint; this->members = decldefs; this->overnext = NULL; this->overroot = NULL; this->funcroot = NULL; this->onemember = NULL; this->literal = literal; this->ismixin = ismixin; this->isstatic = true; this->previous = NULL; this->protection = Prot(PROTundefined); this->instances = NULL; // Compute in advance for Ddoc's use // Bugzilla 11153: ident could be NULL if parsing fails. if (members && ident) { Dsymbol *s; if (Dsymbol::oneMembers(members, &s, ident) && s) { onemember = s; s->parent = this; } } } Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *) { //printf("TemplateDeclaration::syntaxCopy()\n"); TemplateParameters *p = NULL; if (parameters) { p = new TemplateParameters(); p->setDim(parameters->dim); for (size_t i = 0; i < p->dim; i++) (*p)[i] = (*parameters)[i]->syntaxCopy(); } return new TemplateDeclaration(loc, ident, p, constraint ? constraint->syntaxCopy() : NULL, Dsymbol::arraySyntaxCopy(members), ismixin, literal); } void TemplateDeclaration::semantic(Scope *sc) { if (semanticRun != PASSinit) return; // semantic() already run // Remember templates defined in module object that we need to know about if (sc->_module && sc->_module->ident == Id::object) { if (ident == Id::RTInfo) Type::rtinfo = this; } /* Remember Scope for later instantiations, but make * a copy since attributes can change. */ if (!this->_scope) { this->_scope = sc->copy(); this->_scope->setNoFree(); } semanticRun = PASSsemantic; parent = sc->parent; protection = sc->protection; isstatic = toParent()->isModule() || (_scope->stc & STCstatic); if (!isstatic) { if (AggregateDeclaration *ad = parent->pastMixin()->isAggregateDeclaration()) ad->makeNested(); } // Set up scope for parameters ScopeDsymbol *paramsym = new ScopeDsymbol(); paramsym->parent = parent; Scope *paramscope = sc->push(paramsym); paramscope->stc = 0; if (global.params.doDocComments) { origParameters = new TemplateParameters(); origParameters->setDim(parameters->dim); for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; (*origParameters)[i] = tp->syntaxCopy(); } } for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; if (!tp->declareParameter(paramscope)) { error(tp->loc, "parameter '%s' multiply defined", tp->ident->toChars()); errors = true; } if (!tp->semantic(paramscope, parameters)) { errors = true; } if (i + 1 != parameters->dim && tp->isTemplateTupleParameter()) { error("template tuple parameter must be last one"); errors = true; } } /* Calculate TemplateParameter::dependent */ TemplateParameters tparams; tparams.setDim(1); for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; tparams[0] = tp; for (size_t j = 0; j < parameters->dim; j++) { // Skip cases like: X(T : T) if (i == j) continue; if (TemplateTypeParameter *ttp = (*parameters)[j]->isTemplateTypeParameter()) { if (reliesOnTident(ttp->specType, &tparams)) tp->dependent = true; } else if (TemplateAliasParameter *tap = (*parameters)[j]->isTemplateAliasParameter()) { if (reliesOnTident(tap->specType, &tparams) || reliesOnTident(isType(tap->specAlias), &tparams)) { tp->dependent = true; } } } } paramscope->pop(); // Compute again onemember = NULL; if (members) { Dsymbol *s; if (Dsymbol::oneMembers(members, &s, ident) && s) { onemember = s; s->parent = this; } } /* BUG: should check: * o no virtual functions or non-static data members of classes */ semanticRun = PASSsemanticdone; } const char *TemplateDeclaration::kind() const { return (onemember && onemember->isAggregateDeclaration()) ? onemember->kind() : "template"; } /********************************** * Overload existing TemplateDeclaration 'this' with the new one 's'. * Return true if successful; i.e. no conflict. */ bool TemplateDeclaration::overloadInsert(Dsymbol *s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { if (funcroot) return funcroot->overloadInsert(fd); funcroot = fd; return funcroot->overloadInsert(this); } TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) return false; TemplateDeclaration *pthis = this; TemplateDeclaration **ptd; for (ptd = &pthis; *ptd; ptd = &(*ptd)->overnext) { } td->overroot = this; *ptd = td; return true; } /**************************** * Check to see if constraint is satisfied. */ bool TemplateDeclaration::evaluateConstraint( TemplateInstance *ti, Scope *sc, Scope *paramscope, Objects *dedargs, FuncDeclaration *fd) { /* Detect recursive attempts to instantiate this template declaration, * Bugzilla 4072 * void foo(T)(T x) if (is(typeof(foo(x)))) { } * static assert(!is(typeof(foo(7)))); * Recursive attempts are regarded as a constraint failure. */ /* There's a chicken-and-egg problem here. We don't know yet if this template * instantiation will be a local one (enclosing is set), and we won't know until * after selecting the correct template. Thus, function we're nesting inside * is not on the sc scope chain, and this can cause errors in FuncDeclaration::getLevel(). * Workaround the problem by setting a flag to relax the checking on frame errors. */ for (TemplatePrevious *p = previous; p; p = p->prev) { if (arrayObjectMatch(p->dedargs, dedargs)) { //printf("recursive, no match p->sc=%p %p %s\n", p->sc, this, this->toChars()); /* It must be a subscope of p->sc, other scope chains are not recursive * instantiations. */ for (Scope *scx = sc; scx; scx = scx->enclosing) { if (scx == p->sc) return false; } } /* BUG: should also check for ref param differences */ } TemplatePrevious pr; pr.prev = previous; pr.sc = paramscope; pr.dedargs = dedargs; previous = ≺ // add this to threaded list Scope *scx = paramscope->push(ti); scx->parent = ti; scx->tinst = NULL; scx->minst = NULL; assert(!ti->symtab); if (fd) { /* Declare all the function parameters as variables and add them to the scope * Making parameters is similar to FuncDeclaration::semantic3 */ TypeFunction *tf = (TypeFunction *)fd->type; assert(tf->ty == Tfunction); scx->parent = fd; Parameters *fparameters = tf->parameters; int fvarargs = tf->varargs; size_t nfparams = Parameter::dim(fparameters); for (size_t i = 0; i < nfparams; i++) { Parameter *fparam = Parameter::getNth(fparameters, i); fparam->storageClass &= (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor); fparam->storageClass |= STCparameter; if (fvarargs == 2 && i + 1 == nfparams) fparam->storageClass |= STCvariadic; } for (size_t i = 0; i < fparameters->dim; i++) { Parameter *fparam = (*fparameters)[i]; if (!fparam->ident) continue; // don't add it, if it has no name VarDeclaration *v = new VarDeclaration(loc, fparam->type, fparam->ident, NULL); v->storage_class = fparam->storageClass; v->semantic(scx); if (!ti->symtab) ti->symtab = new DsymbolTable(); if (!scx->insert(v)) error("parameter %s.%s is already defined", toChars(), v->toChars()); else v->parent = fd; } if (isstatic) fd->storage_class |= STCstatic; fd->vthis = fd->declareThis(scx, fd->isThis()); } Expression *e = constraint->syntaxCopy(); assert(ti->inst == NULL); ti->inst = ti; // temporary instantiation to enable genIdent() scx->flags |= SCOPEconstraint; bool errors = false; bool result = evalStaticCondition(scx, constraint, e, errors); ti->inst = NULL; ti->symtab = NULL; scx = scx->pop(); previous = pr.prev; // unlink from threaded list if (errors) return false; return result; } /*************************************** * 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(Scope *sc, TemplateInstance *ti, Objects *dedtypes, Expressions *fargs, int flag) { MATCH m; size_t dedtypes_dim = dedtypes->dim; dedtypes->zero(); if (errors) return MATCHnomatch; size_t parameters_dim = parameters->dim; int variadic = isVariadic() != NULL; // If more arguments than parameters, no match if (ti->tiargs->dim > parameters_dim && !variadic) { return MATCHnomatch; } assert(dedtypes_dim == parameters_dim); assert(dedtypes_dim >= ti->tiargs->dim || variadic); assert(_scope); // Set up scope for template parameters ScopeDsymbol *paramsym = new ScopeDsymbol(); paramsym->parent = _scope->parent; Scope *paramscope = _scope->push(paramsym); paramscope->tinst = ti; paramscope->minst = sc->minst; paramscope->callsc = sc; paramscope->stc = 0; // Attempt type deduction m = MATCHexact; for (size_t i = 0; i < dedtypes_dim; i++) { MATCH m2; TemplateParameter *tp = (*parameters)[i]; Declaration *sparam; //printf("\targument [%d]\n", i); m2 = tp->matchArg(ti->loc, paramscope, ti->tiargs, i, parameters, dedtypes, &sparam); //printf("\tm2 = %d\n", m2); if (m2 == MATCHnomatch) { goto Lnomatch; } if (m2 < m) m = m2; if (!flag) sparam->semantic(paramscope); if (!paramscope->insert(sparam)) // TODO: This check can make more early goto Lnomatch; // in TemplateDeclaration::semantic, and // then we don't need to make sparam if flags == 0 } if (!flag) { /* Any parameter left without a type gets the type of * its corresponding arg */ for (size_t i = 0; i < dedtypes_dim; i++) { if (!(*dedtypes)[i]) { assert(i < ti->tiargs->dim); (*dedtypes)[i] = (Type *)(*ti->tiargs)[i]; } } } if (m > MATCHnomatch && constraint && !flag) { if (ti->hasNestedArgs(ti->tiargs, this->isstatic)) // TODO: should gag error ti->parent = ti->enclosing; else ti->parent = this->parent; // Similar to doHeaderInstantiation FuncDeclaration *fd = onemember ? onemember->isFuncDeclaration() : NULL; if (fd) { assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type->syntaxCopy(); fd = new FuncDeclaration(fd->loc, fd->endloc, fd->ident, fd->storage_class, tf); fd->parent = ti; fd->inferRetType = true; // Shouldn't run semantic on default arguments and return type. for (size_t i = 0; i < tf->parameters->dim; i++) (*tf->parameters)[i]->defaultArg = NULL; tf->next = NULL; // Resolve parameter types and 'auto ref's. tf->fargs = fargs; unsigned olderrors = global.startGagging(); fd->type = tf->semantic(loc, paramscope); if (global.endGagging(olderrors)) { assert(fd->type->ty != Tfunction); goto Lnomatch; } assert(fd->type->ty == Tfunction); fd->originalType = fd->type; // for mangling } // TODO: dedtypes => ti->tiargs ? if (!evaluateConstraint(ti, sc, paramscope, dedtypes, fd)) goto Lnomatch; } goto Lret; Lnomatch: m = MATCHnomatch; Lret: paramscope->pop(); 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(Scope *sc, TemplateDeclaration *td2, Expressions *fargs) { /* 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(Loc(), ident); // create dummy template instance // Set type arguments to dummy template instance to be types // generated from the parameters to this template declaration ti.tiargs = new Objects(); ti.tiargs->reserve(parameters->dim); for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; if (tp->dependent) break; RootObject *p = (RootObject *)tp->dummyArg(); if (!p) break; ti.tiargs->push(p); } // Temporary Array to hold deduced types Objects dedtypes; dedtypes.setDim(td2->parameters->dim); // Attempt a type deduction MATCH m = td2->matchWithInstance(sc, &ti, &dedtypes, fargs, 1); if (m > MATCHnomatch) { /* A non-variadic template is more specialized than a * variadic one. */ TemplateTupleParameter *tp = isVariadic(); if (tp && !tp->dependent && !td2->isVariadic()) goto L1; return m; } L1: return MATCHnomatch; } static Expression *emptyArrayElement = NULL; class TypeDeduced : public Type { public: Type *tded; Expressions argexps; // corresponding expressions Types tparams; // tparams[i]->mod TypeDeduced(Type *tt, Expression *e, Type *tparam) : Type(Tnone) { tded = tt; argexps.push(e); tparams.push(tparam); } virtual ~TypeDeduced() { } void update(Expression *e, Type *tparam) { argexps.push(e); tparams.push(tparam); } void update(Type *tt, Expression *e, Type *tparam) { tded = tt; argexps.push(e); tparams.push(tparam); } MATCH matchAll(Type *tt) { MATCH match = MATCHexact; for (size_t j = 0; j < argexps.dim; j++) { Expression *e = argexps[j]; assert(e); if (e == emptyArrayElement) continue; Type *t = tt->addMod(tparams[j]->mod)->substWildTo(MODconst); MATCH m = e->implicitConvTo(t); if (match > m) match = m; if (match <= MATCHnomatch) break; } return match; } }; /************************************************* * Match function arguments against a specific template function. * Input: * ti * sc instantiation scope * fd * tthis 'this' argument if !NULL * fargs arguments to function * Output: * fd Partially instantiated function declaration * ti->tdtypes Expression/Type deduced template arguments * Returns: * match level * bit 0-3 Match template parameters by inferred template arguments * bit 4-7 Match template parameters by initial template arguments */ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( TemplateInstance *ti, Scope *sc, FuncDeclaration *&fd, Type *tthis, Expressions *fargs) { size_t nfparams; size_t nfargs; size_t ntargs; // array size of tiargs size_t fptupindex = IDX_NOTFOUND; MATCH match = MATCHexact; MATCH matchTiargs = MATCHexact; Parameters *fparameters; // function parameter list int fvarargs; // function varargs unsigned wildmatch = 0; size_t inferStart = 0; Loc instLoc = ti->loc; Objects *tiargs = ti->tiargs; Objects *dedargs = new Objects(); Objects* dedtypes = &ti->tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T assert(_scope); dedargs->setDim(parameters->dim); dedargs->zero(); dedtypes->setDim(parameters->dim); dedtypes->zero(); if (errors || fd->errors) return MATCHnomatch; // Set up scope for parameters ScopeDsymbol *paramsym = new ScopeDsymbol(); paramsym->parent = _scope->parent; // should use hasnestedArgs and enclosing? Scope *paramscope = _scope->push(paramsym); paramscope->tinst = ti; paramscope->minst = sc->minst; paramscope->callsc = sc; paramscope->stc = 0; TemplateTupleParameter *tp = isVariadic(); Tuple *declaredTuple = NULL; ntargs = 0; if (tiargs) { // Set initial template arguments ntargs = tiargs->dim; size_t n = parameters->dim; if (tp) n--; if (ntargs > n) { if (!tp) goto Lnomatch; /* The extra initial template arguments * now form the tuple argument. */ Tuple *t = new Tuple(); assert(parameters->dim); (*dedargs)[parameters->dim - 1] = t; t->objects.setDim(ntargs - n); for (size_t i = 0; i < t->objects.dim; i++) { t->objects[i] = (*tiargs)[n + i]; } declareParameter(paramscope, tp, t); declaredTuple = t; } else n = ntargs; memcpy(dedargs->tdata(), tiargs->tdata(), n * sizeof(*dedargs->tdata())); for (size_t i = 0; i < n; i++) { assert(i < parameters->dim); Declaration *sparam = NULL; MATCH m = (*parameters)[i]->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, &sparam); //printf("\tdeduceType m = %d\n", m); if (m <= MATCHnomatch) goto Lnomatch; if (m < matchTiargs) matchTiargs = m; sparam->semantic(paramscope); if (!paramscope->insert(sparam)) goto Lnomatch; } if (n < parameters->dim && !declaredTuple) { inferStart = n; } else inferStart = parameters->dim; //printf("tiargs matchTiargs = %d\n", matchTiargs); } fparameters = fd->getParameters(&fvarargs); nfparams = Parameter::dim(fparameters); // number of function parameters nfargs = fargs ? fargs->dim : 0; // number of function arguments /* Check for match of function arguments with variadic template * parameter, such as: * * void foo(T, A...)(T t, A a); * void main() { foo(1,2,3); } */ if (tp) // if variadic { // TemplateTupleParameter always makes most lesser matching. matchTiargs = MATCHconvert; if (nfparams == 0 && nfargs != 0) // if no function parameters { if (!declaredTuple) { Tuple *t = new Tuple(); //printf("t = %p\n", t); (*dedargs)[parameters->dim - 1] = t; declareParameter(paramscope, tp, t); declaredTuple = t; } } 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++) { Parameter *fparam = (*fparameters)[fptupindex]; if (fparam->type->ty != Tident) continue; TypeIdentifier *tid = (TypeIdentifier *)fparam->type; if (!tp->ident->equals(tid->ident) || tid->idents.dim) continue; if (fvarargs) // variadic function doesn't goto Lnomatch; // go with variadic template goto L1; } fptupindex = IDX_NOTFOUND; L1: ; } } if (toParent()->isModule() || (_scope->stc & STCstatic)) tthis = NULL; if (tthis) { bool hasttp = false; // Match 'tthis' to any TemplateThisParameter's for (size_t i = 0; i < parameters->dim; i++) { TemplateThisParameter *ttp = (*parameters)[i]->isTemplateThisParameter(); if (ttp) { hasttp = true; Type *t = new TypeIdentifier(Loc(), ttp->ident); MATCH m = deduceType(tthis, paramscope, t, parameters, dedtypes); if (m <= MATCHnomatch) goto Lnomatch; if (m < match) match = m; // pick worst match } } // Match attributes of tthis against attributes of fd if (fd->type && !fd->isCtorDeclaration()) { StorageClass stc = _scope->stc | fd->storage_class2; // Propagate parent storage class (see bug 5504) Dsymbol *p = parent; while (p->isTemplateDeclaration() || p->isTemplateInstance()) p = p->parent; AggregateDeclaration *ad = p->isAggregateDeclaration(); if (ad) stc |= ad->storage_class; unsigned char mod = fd->type->mod; if (stc & STCimmutable) mod = MODimmutable; else { if (stc & (STCshared | STCsynchronized)) mod |= MODshared; if (stc & STCconst) mod |= MODconst; if (stc & STCwild) mod |= MODwild; } unsigned char thismod = tthis->mod; if (hasttp) mod = MODmerge(thismod, mod); MATCH m = MODmethodConv(thismod, mod); if (m <= MATCHnomatch) goto Lnomatch; if (m < match) match = m; } } // Loop through the function parameters { //printf("%s\n\tnfargs = %d, nfparams = %d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple->objects.dim : 0); //printf("\ttp = %p, fptupindex = %d, found = %d, declaredTuple = %s\n", tp, fptupindex, fptupindex != IDX_NOTFOUND, declaredTuple ? declaredTuple->toChars() : NULL); size_t argi = 0; size_t nfargs2 = nfargs; // nfargs + supplied defaultArgs for (size_t parami = 0; parami < nfparams; parami++) { Parameter *fparam = Parameter::getNth(fparameters, parami); // Apply function parameter storage classes to parameter types Type *prmtype = fparam->type->addStorageClass(fparam->storageClass); Expression *farg; /* See function parameters which wound up * as part of a template tuple parameter. */ if (fptupindex != IDX_NOTFOUND && parami == fptupindex) { assert(prmtype->ty == Tident); TypeIdentifier *tid = (TypeIdentifier *)prmtype; if (!declaredTuple) { /* The types of the function arguments * now form the tuple argument. */ declaredTuple = new Tuple(); (*dedargs)[parameters->dim - 1] = declaredTuple; /* Count function parameters following a tuple parameter. * void foo(U, T...)(int y, T, U, int) {} // rem == 2 (U, int) */ size_t rem = 0; for (size_t j = parami + 1; j < nfparams; j++) { Parameter *p = Parameter::getNth(fparameters, j); if (!reliesOnTident(p->type, parameters, inferStart)) { Type *pt = p->type->syntaxCopy()->semantic(fd->loc, paramscope); rem += pt->ty == Ttuple ? ((TypeTuple *)pt)->arguments->dim : 1; } else { ++rem; } } if (nfargs2 - argi < rem) goto Lnomatch; declaredTuple->objects.setDim(nfargs2 - argi - rem); for (size_t i = 0; i < declaredTuple->objects.dim; i++) { farg = (*fargs)[argi + i]; // Check invalid arguments to detect errors early. if (farg->op == TOKerror || farg->type->ty == Terror) goto Lnomatch; if (!(fparam->storageClass & STClazy) && farg->type->ty == Tvoid) goto Lnomatch; Type *tt; MATCH m; if (unsigned char wm = deduceWildHelper(farg->type, &tt, tid)) { wildmatch |= wm; m = MATCHconst; } else { m = deduceTypeHelper(farg->type, &tt, tid); } if (m <= MATCHnomatch) goto Lnomatch; if (m < match) match = m; /* Remove top const for dynamic array types and pointer types */ if ((tt->ty == Tarray || tt->ty == Tpointer) && !tt->isMutable() && (!(fparam->storageClass & STCref) || ((fparam->storageClass & STCauto) && !farg->isLvalue()))) { tt = tt->mutableOf(); } declaredTuple->objects[i] = tt; } declareParameter(paramscope, tp, declaredTuple); } else { // Bugzilla 6810: If declared tuple is not a type tuple, // it cannot be function parameter types. for (size_t i = 0; i < declaredTuple->objects.dim; i++) { if (!isType(declaredTuple->objects[i])) goto Lnomatch; } } assert(declaredTuple); argi += declaredTuple->objects.dim; continue; } // If parameter type doesn't depend on inferred template parameters, // semantic it to get actual type. if (!reliesOnTident(prmtype, parameters, inferStart)) { // should copy prmtype to avoid affecting semantic result prmtype = prmtype->syntaxCopy()->semantic(fd->loc, paramscope); if (prmtype->ty == Ttuple) { TypeTuple *tt = (TypeTuple *)prmtype; size_t tt_dim = tt->arguments->dim; for (size_t j = 0; j < tt_dim; j++, ++argi) { Parameter *p = (*tt->arguments)[j]; if (j == tt_dim - 1 && fvarargs == 2 && parami + 1 == nfparams && argi < nfargs) { prmtype = p->type; goto Lvarargs; } if (argi >= nfargs) { if (p->defaultArg) continue; goto Lnomatch; } farg = (*fargs)[argi]; if (!farg->implicitConvTo(p->type)) goto Lnomatch; } continue; } } if (argi >= nfargs) // if not enough arguments { if (!fparam->defaultArg) goto Lvarargs; /* Bugzilla 2803: Before the starting of type deduction from the function * default arguments, set the already deduced parameters into paramscope. * It's necessary to avoid breaking existing acceptable code. Cases: * * 1. Already deduced template parameters can appear in fparam->defaultArg: * auto foo(A, B)(A a, B b = A.stringof); * foo(1); * // at fparam == 'B b = A.string', A is equivalent with the deduced type 'int' * * 2. If prmtype depends on default-specified template parameter, the * default type should be preferred. * auto foo(N = size_t, R)(R r, N start = 0) * foo([1,2,3]); * // at fparam `N start = 0`, N should be 'size_t' before * // the deduction result from fparam->defaultArg. */ if (argi == nfargs) { for (size_t i = 0; i < dedtypes->dim; i++) { Type *at = isType((*dedtypes)[i]); if (at && at->ty == Tnone) { TypeDeduced *xt = (TypeDeduced *)at; (*dedtypes)[i] = xt->tded; // 'unbox' delete xt; } } for (size_t i = ntargs; i < dedargs->dim; i++) { TemplateParameter *tparam = (*parameters)[i]; RootObject *oarg = (*dedargs)[i]; RootObject *oded = (*dedtypes)[i]; if (!oarg) { if (oded) { if (tparam->specialization() || !tparam->isTemplateTypeParameter()) { /* The specialization can work as long as afterwards * the oded == oarg */ (*dedargs)[i] = oded; MATCH m2 = tparam->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, NULL); //printf("m2 = %d\n", m2); if (m2 <= MATCHnomatch) goto Lnomatch; if (m2 < matchTiargs) matchTiargs = m2; // pick worst match if (!(*dedtypes)[i]->equals(oded)) error("specialization not allowed for deduced parameter %s", tparam->ident->toChars()); } else { if (MATCHconvert < matchTiargs) matchTiargs = MATCHconvert; } (*dedargs)[i] = declareParameter(paramscope, tparam, oded); } else { oded = tparam->defaultArg(instLoc, paramscope); if (oded) (*dedargs)[i] = declareParameter(paramscope, tparam, oded); } } } } nfargs2 = argi + 1; /* If prmtype does not depend on any template parameters: * * auto foo(T)(T v, double x = 0); * foo("str"); * // at fparam == 'double x = 0' * * or, if all template parameters in the prmtype are already deduced: * * auto foo(R)(R range, ElementType!R sum = 0); * foo([1,2,3]); * // at fparam == 'ElementType!R sum = 0' * * Deducing prmtype from fparam->defaultArg is not necessary. */ if (prmtype->deco || prmtype->syntaxCopy()->trySemantic(loc, paramscope)) { ++argi; continue; } // Deduce prmtype from the defaultArg. farg = fparam->defaultArg->syntaxCopy(); farg = ::semantic(farg, paramscope); farg = resolveProperties(paramscope, farg); } else { farg = (*fargs)[argi]; } { // Check invalid arguments to detect errors early. if (farg->op == TOKerror || farg->type->ty == Terror) goto Lnomatch; Type *att = NULL; Lretry: Type *argtype = farg->type; if (!(fparam->storageClass & STClazy) && argtype->ty == Tvoid && farg->op != TOKfunction) goto Lnomatch; // Bugzilla 12876: optimize arugument to allow CT-known length matching farg = farg->optimize(WANTvalue, (fparam->storageClass & (STCref | STCout)) != 0); //printf("farg = %s %s\n", farg->type->toChars(), farg->toChars()); RootObject *oarg = farg; if ((fparam->storageClass & STCref) && (!(fparam->storageClass & STCauto) || farg->isLvalue())) { /* Allow expressions that have CT-known boundaries and type [] to match with [dim] */ Type *taai; if (argtype->ty == Tarray && (prmtype->ty == Tsarray || (prmtype->ty == Taarray && (taai = ((TypeAArray *)prmtype)->index)->ty == Tident && ((TypeIdentifier *)taai)->idents.dim == 0))) { if (farg->op == TOKstring) { StringExp *se = (StringExp *)farg; argtype = se->type->nextOf()->sarrayOf(se->len); } else if (farg->op == TOKarrayliteral) { ArrayLiteralExp *ae = (ArrayLiteralExp *)farg; argtype = ae->type->nextOf()->sarrayOf(ae->elements->dim); } else if (farg->op == TOKslice) { SliceExp *se = (SliceExp *)farg; if (Type *tsa = toStaticArrayType(se)) argtype = tsa; } } oarg = argtype; } else if ((fparam->storageClass & STCout) == 0 && (argtype->ty == Tarray || argtype->ty == Tpointer) && templateParameterLookup(prmtype, parameters) != IDX_NOTFOUND && ((TypeIdentifier *)prmtype)->idents.dim == 0) { /* The farg passing to the prmtype always make a copy. Therefore, * we can shrink the set of the deduced type arguments for prmtype * by adjusting top-qualifier of the argtype. * * prmtype argtype ta * T <- const(E)[] const(E)[] * T <- const(E[]) const(E)[] * qualifier(T) <- const(E)[] const(E[]) * qualifier(T) <- const(E[]) const(E[]) */ Type *ta = argtype->castMod(prmtype->mod ? argtype->nextOf()->mod : 0); if (ta != argtype) { Expression *ea = farg->copy(); ea->type = ta; oarg = ea; } } if (fvarargs == 2 && parami + 1 == nfparams && argi + 1 < nfargs) goto Lvarargs; unsigned wm = 0; MATCH m = deduceType(oarg, paramscope, prmtype, parameters, dedtypes, &wm, inferStart); //printf("\tL%d deduceType m = %d, wm = x%x, wildmatch = x%x\n", __LINE__, m, wm, wildmatch); wildmatch |= wm; /* If no match, see if the argument can be matched by using * implicit conversions. */ if (m == MATCHnomatch && prmtype->deco) m = farg->implicitConvTo(prmtype); if (m == MATCHnomatch) { AggregateDeclaration *ad = isAggregate(farg->type); if (ad && ad->aliasthis && argtype != att) { if (!att && argtype->checkAliasThisRec()) // Bugzilla 12537 att = argtype; /* If a semantic error occurs while doing alias this, * eg purity(bug 7295), just regard it as not a match. */ if (Expression *e = resolveAliasThis(sc, farg, true)) { farg = e; goto Lretry; } } } if (m > MATCHnomatch && (fparam->storageClass & (STCref | STCauto)) == STCref) { if (!farg->isLvalue()) { if ((farg->op == TOKstring || farg->op == TOKslice) && (prmtype->ty == Tsarray || prmtype->ty == Taarray)) { // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] } else goto Lnomatch; } } if (m > MATCHnomatch && (fparam->storageClass & STCout)) { if (!farg->isLvalue()) goto Lnomatch; if (!farg->type->isMutable()) // Bugzilla 11916 goto Lnomatch; } if (m == MATCHnomatch && (fparam->storageClass & STClazy) && prmtype->ty == Tvoid && farg->type->ty != Tvoid) m = MATCHconvert; if (m != MATCHnomatch) { if (m < match) match = m; // pick worst match argi++; continue; } } Lvarargs: /* The following code for variadic arguments closely * matches TypeFunction::callMatch() */ if (!(fvarargs == 2 && parami + 1 == nfparams)) goto Lnomatch; /* Check for match with function parameter T... */ Type *tb = prmtype->toBasetype(); switch (tb->ty) { // 6764 fix - TypeAArray may be TypeSArray have not yet run semantic(). case Tsarray: case Taarray: // Perhaps we can do better with this, see TypeFunction::callMatch() if (tb->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)tb; dinteger_t sz = tsa->dim->toInteger(); if (sz != nfargs - argi) goto Lnomatch; } else if (tb->ty == Taarray) { TypeAArray *taa = (TypeAArray *)tb; Expression *dim = new IntegerExp(instLoc, nfargs - argi, Type::tsize_t); size_t i = templateParameterLookup(taa->index, parameters); if (i == IDX_NOTFOUND) { Expression *e; Type *t; Dsymbol *s; Scope *sco; unsigned errors = global.startGagging(); /* ref: https://issues.dlang.org/show_bug.cgi?id=11118 * The parameter isn't part of the template * ones, let's try to find it in the * instantiation scope 'sc' and the one * belonging to the template itself. */ sco = sc; taa->index->resolve(instLoc, sco, &e, &t, &s); if (!e) { sco = paramscope; taa->index->resolve(instLoc, sco, &e, &t, &s); } global.endGagging(errors); if (!e) { goto Lnomatch; } e = e->ctfeInterpret(); e = e->implicitCastTo(sco, Type::tsize_t); e = e->optimize(WANTvalue); if (!dim->equals(e)) goto Lnomatch; } else { // This code matches code in TypeInstance::deduceType() TemplateParameter *tprm = (*parameters)[i]; TemplateValueParameter *tvp = tprm->isTemplateValueParameter(); if (!tvp) goto Lnomatch; Expression *e = (Expression *)(*dedtypes)[i]; if (e) { if (!dim->equals(e)) goto Lnomatch; } else { Type *vt = tvp->valType->semantic(Loc(), sc); MATCH m = (MATCH)dim->implicitConvTo(vt); if (m <= MATCHnomatch) goto Lnomatch; (*dedtypes)[i] = dim; } } } /* fall through */ case Tarray: { TypeArray *ta = (TypeArray *)tb; Type *tret = fparam->isLazyArray(); for (; argi < nfargs; argi++) { Expression *arg = (*fargs)[argi]; assert(arg); MATCH m; /* If lazy array of delegates, * convert arg(s) to delegate(s) */ 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 { unsigned wm = 0; m = deduceType(arg, paramscope, ta->next, parameters, dedtypes, &wm, inferStart); wildmatch |= wm; } if (m == MATCHnomatch) goto Lnomatch; if (m < match) match = m; } goto Lmatch; } case Tclass: case Tident: goto Lmatch; default: goto Lnomatch; } ++argi; } //printf("-> argi = %d, nfargs = %d, nfargs2 = %d\n", argi, nfargs, nfargs2); if (argi != nfargs2 && !fvarargs) goto Lnomatch; } Lmatch: for (size_t i = 0; i < dedtypes->dim; i++) { Type *at = isType((*dedtypes)[i]); if (at) { if (at->ty == Tnone) { TypeDeduced *xt = (TypeDeduced *)at; at = xt->tded; // 'unbox' delete xt; } (*dedtypes)[i] = at->merge2(); } } for (size_t i = ntargs; i < dedargs->dim; i++) { TemplateParameter *tparam = (*parameters)[i]; //printf("tparam[%d] = %s\n", i, tparam->ident->toChars()); /* For T:T*, the dedargs is the T*, dedtypes is the T * But for function templates, we really need them to match */ RootObject *oarg = (*dedargs)[i]; RootObject *oded = (*dedtypes)[i]; //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded); //if (oarg) printf("oarg: %s\n", oarg->toChars()); //if (oded) printf("oded: %s\n", oded->toChars()); if (!oarg) { if (oded) { if (tparam->specialization() || !tparam->isTemplateTypeParameter()) { /* The specialization can work as long as afterwards * the oded == oarg */ (*dedargs)[i] = oded; MATCH m2 = tparam->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, NULL); //printf("m2 = %d\n", m2); if (m2 <= MATCHnomatch) goto Lnomatch; if (m2 < matchTiargs) matchTiargs = m2; // pick worst match if (!(*dedtypes)[i]->equals(oded)) error("specialization not allowed for deduced parameter %s", tparam->ident->toChars()); } else { if (MATCHconvert < matchTiargs) matchTiargs = MATCHconvert; } } else { oded = tparam->defaultArg(instLoc, paramscope); if (!oded) { // if tuple parameter and // tuple parameter was not in function parameter list and // we're one or more arguments short (i.e. no tuple argument) if (tparam == tp && fptupindex == IDX_NOTFOUND && ntargs <= dedargs->dim - 1) { // make tuple argument an empty tuple oded = (RootObject *)new Tuple(); } else goto Lnomatch; } if (isError(oded)) goto Lerror; ntargs++; /* At the template parameter T, the picked default template argument * X!int should be matched to T in order to deduce dependent * template parameter A. * auto foo(T : X!A = X!int, A...)() { ... } * foo(); // T <-- X!int, A <-- (int) */ if (tparam->specialization()) { (*dedargs)[i] = oded; MATCH m2 = tparam->matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, NULL); //printf("m2 = %d\n", m2); if (m2 <= MATCHnomatch) goto Lnomatch; if (m2 < matchTiargs) matchTiargs = m2; // pick worst match if (!(*dedtypes)[i]->equals(oded)) error("specialization not allowed for deduced parameter %s", tparam->ident->toChars()); } } oded = declareParameter(paramscope, tparam, oded); (*dedargs)[i] = oded; } } /* Bugzilla 7469: As same as the code for 7469 in findBestMatch, * expand a Tuple in dedargs to normalize template arguments. */ if (size_t d = dedargs->dim) { if (Tuple *va = isTuple((*dedargs)[d - 1])) { if (va->objects.dim) { dedargs->setDim(d - 1); dedargs->insert(d - 1, &va->objects); } } } ti->tiargs = dedargs; // update to the normalized template arguments. // Partially instantiate function for constraint and fd->leastAsSpecialized() { assert(paramsym); Scope *sc2 = _scope; sc2 = sc2->push(paramsym); sc2 = sc2->push(ti); sc2->parent = ti; sc2->tinst = ti; sc2->minst = sc->minst; fd = doHeaderInstantiation(ti, sc2, fd, tthis, fargs); sc2 = sc2->pop(); sc2 = sc2->pop(); if (!fd) goto Lnomatch; } if (constraint) { if (!evaluateConstraint(ti, sc, paramscope, dedargs, fd)) goto Lnomatch; } paramscope->pop(); //printf("\tmatch %d\n", match); return (MATCH)(match | (matchTiargs<<4)); Lnomatch: paramscope->pop(); //printf("\tnomatch\n"); return MATCHnomatch; Lerror: // todo: for the future improvement paramscope->pop(); //printf("\terror\n"); return MATCHnomatch; } /************************************************** * Declare template parameter tp with value o, and install it in the scope sc. */ RootObject *TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, RootObject *o) { //printf("TemplateDeclaration::declareParameter('%s', o = %p)\n", tp->ident->toChars(), o); Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Tuple *va = isTuple(o); Declaration *d; VarDeclaration *v = NULL; if (ea && ea->op == TOKtype) ta = ea->type; else if (ea && ea->op == TOKscope) sa = ((ScopeExp *)ea)->sds; else if (ea && (ea->op == TOKthis || ea->op == TOKsuper)) sa = ((ThisExp *)ea)->var; else if (ea && ea->op == TOKfunction) { if (((FuncExp *)ea)->td) sa = ((FuncExp *)ea)->td; else sa = ((FuncExp *)ea)->fd; } if (ta) { //printf("type %s\n", ta->toChars()); d = new AliasDeclaration(Loc(), tp->ident, ta); } else if (sa) { //printf("Alias %s %s;\n", sa->ident->toChars(), tp->ident->toChars()); d = new AliasDeclaration(Loc(), 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; v = new VarDeclaration(loc, t, tp->ident, init); v->storage_class = STCmanifest | STCtemplateparameter; d = v; } else if (va) { //printf("\ttuple\n"); d = new TupleDeclaration(loc, tp->ident, &va->objects); } else { assert(0); } d->storage_class |= STCtemplateparameter; if (ta) { Type *t = ta; // consistent with Type::checkDeprecated() while (t->ty != Tenum) { if (!t->nextOf()) break; t = ((TypeNext *)t)->next; } if (Dsymbol *s = t->toDsymbol(sc)) { if (s->isDeprecated()) d->storage_class |= STCdeprecated; } } else if (sa) { if (sa->isDeprecated()) d->storage_class |= STCdeprecated; } if (!sc->insert(d)) error("declaration %s is already defined", tp->ident->toChars()); d->semantic(sc); /* So the caller's o gets updated with the result of semantic() being run on o */ if (v) o = initializerToExpression(v->_init); return o; } /************************************** * Determine if TemplateDeclaration is variadic. */ TemplateTupleParameter *isVariadic(TemplateParameters *parameters) { size_t dim = parameters->dim; TemplateTupleParameter *tp = NULL; if (dim) tp = ((*parameters)[dim - 1])->isTemplateTupleParameter(); return tp; } TemplateTupleParameter *TemplateDeclaration::isVariadic() { return ::isVariadic(parameters); } /*********************************** * We can overload templates. */ bool TemplateDeclaration::isOverloadable() { return true; } /************************************************* * Given function arguments, figure out which template function * to expand, and return matching result. * Input: * m matching result * dstart the root of overloaded function templates * loc instantiation location * sc instantiation scope * tiargs initial list of template arguments * tthis if !NULL, the 'this' pointer argument * fargs arguments to function */ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, Objects *tiargs, Type *tthis, Expressions *fargs) { struct ParamDeduce { // context Loc loc; Scope *sc; Type *tthis; Objects *tiargs; Expressions *fargs; // result Match *m; int property; // 0: unintialized // 1: seen @property // 2: not @property size_t ov_index; TemplateDeclaration *td_best; TemplateInstance *ti_best; MATCH ta_last; Type *tthis_best; static int fp(void *param, Dsymbol *s) { if (s->errors) return 0; if (FuncDeclaration *fd = s->isFuncDeclaration()) return ((ParamDeduce *)param)->applyFunction(fd); if (TemplateDeclaration *td = s->isTemplateDeclaration()) return ((ParamDeduce *)param)->applyTemplate(td); return 0; } int applyFunction(FuncDeclaration *fd) { // skip duplicates if (fd == m->lastf) return 0; // explicitly specified tiargs never match to non template function if (tiargs && tiargs->dim > 0) return 0; // constructors need a valid scope in order to detect semantic errors if (!fd->isCtorDeclaration() && fd->semanticRun < PASSsemanticdone) { Ungag ungag = fd->ungagSpeculative(); fd->semantic(NULL); } if (fd->semanticRun < PASSsemanticdone) { ::error(loc, "forward reference to template %s", fd->toChars()); return 1; } //printf("fd = %s %s, fargs = %s\n", fd->toChars(), fd->type->toChars(), fargs->toChars()); m->anyf = fd; TypeFunction *tf = (TypeFunction *)fd->type; int prop = (tf->isproperty) ? 1 : 2; if (property == 0) property = prop; else if (property != prop) error(fd->loc, "cannot overload both property and non-property functions"); /* For constructors, qualifier check will be opposite direction. * Qualified constructor always makes qualified object, then will be checked * that it is implicitly convertible to tthis. */ Type *tthis_fd = fd->needThis() ? tthis : NULL; bool isCtorCall = tthis_fd && fd->isCtorDeclaration(); if (isCtorCall) { //printf("%s tf->mod = x%x tthis_fd->mod = x%x %d\n", tf->toChars(), // tf->mod, tthis_fd->mod, fd->isolateReturn()); if (MODimplicitConv(tf->mod, tthis_fd->mod) || (tf->isWild() && tf->isShared() == tthis_fd->isShared()) || fd->isolateReturn()) { /* && tf->isShared() == tthis_fd->isShared()*/ // Uniquely constructed object can ignore shared qualifier. // TODO: Is this appropriate? tthis_fd = NULL; } else return 0; // MATCHnomatch } MATCH mfa = tf->callMatch(tthis_fd, fargs); //printf("test1: mfa = %d\n", mfa); if (mfa > MATCHnomatch) { if (mfa > m->last) goto LfIsBetter; if (mfa < m->last) goto LlastIsBetter; /* See if one of the matches overrides the other. */ assert(m->lastf); if (m->lastf->overrides(fd)) goto LlastIsBetter; if (fd->overrides(m->lastf)) goto LfIsBetter; /* Try to disambiguate using template-style partial ordering rules. * In essence, if f() and g() are ambiguous, if f() can call g(), * but g() cannot call f(), then pick f(). * This is because f() is "more specialized." */ { MATCH c1 = fd->leastAsSpecialized(m->lastf); MATCH c2 = m->lastf->leastAsSpecialized(fd); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto LfIsBetter; if (c1 < c2) goto LlastIsBetter; } /* The 'overrides' check above does covariant checking only * for virtual member functions. It should do it for all functions, * but in order to not risk breaking code we put it after * the 'leastAsSpecialized' check. * In the future try moving it before. * I.e. a not-the-same-but-covariant match is preferred, * as it is more restrictive. */ if (!m->lastf->type->equals(fd->type)) { //printf("cov: %d %d\n", m->lastf->type->covariant(fd->type), fd->type->covariant(m->lastf->type)); if (m->lastf->type->covariant(fd->type) == 1) goto LlastIsBetter; if (fd->type->covariant(m->lastf->type) == 1) goto LfIsBetter; } /* If the two functions are the same function, like: * int foo(int); * int foo(int x) { ... } * then pick the one with the body. */ if (tf->equals(m->lastf->type) && fd->storage_class == m->lastf->storage_class && fd->parent == m->lastf->parent && fd->protection == m->lastf->protection && fd->linkage == m->lastf->linkage) { if ( fd->fbody && !m->lastf->fbody) goto LfIsBetter; if (!fd->fbody && m->lastf->fbody) goto LlastIsBetter; } // Bugzilla 14450: Prefer exact qualified constructor for the creating object type if (isCtorCall && tf->mod != m->lastf->type->mod) { if (tthis->mod == tf->mod) goto LfIsBetter; if (tthis->mod == m->lastf->type->mod) goto LlastIsBetter; } m->nextf = fd; m->count++; return 0; LlastIsBetter: return 0; LfIsBetter: td_best = NULL; ti_best = NULL; ta_last = MATCHexact; m->last = mfa; m->lastf = fd; tthis_best = tthis_fd; ov_index = 0; m->count = 1; return 0; } return 0; } int applyTemplate(TemplateDeclaration *td) { //printf("applyTemplate()\n"); // skip duplicates if (td == td_best) return 0; if (!sc) sc = td->_scope; // workaround for Type::aliasthisOf if (td->semanticRun == PASSinit && td->_scope) { // Try to fix forward reference. Ungag errors while doing so. Ungag ungag = td->ungagSpeculative(); td->semantic(td->_scope); } if (td->semanticRun == PASSinit) { ::error(loc, "forward reference to template %s", td->toChars()); Lerror: m->lastf = NULL; m->count = 0; m->last = MATCHnomatch; return 1; } //printf("td = %s\n", td->toChars()); FuncDeclaration *f; f = td->onemember ? td->onemember->isFuncDeclaration() : NULL; if (!f) { if (!tiargs) tiargs = new Objects(); TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); Objects dedtypes; dedtypes.setDim(td->parameters->dim); assert(td->semanticRun != PASSinit); MATCH mta = td->matchWithInstance(sc, ti, &dedtypes, fargs, 0); //printf("matchWithInstance = %d\n", mta); if (mta <= MATCHnomatch || mta < ta_last) // no match or less match return 0; ti->semantic(sc, fargs); if (!ti->inst) // if template failed to expand return 0; Dsymbol *s = ti->inst->toAlias(); FuncDeclaration *fd; if (TemplateDeclaration *tdx = s->isTemplateDeclaration()) { Objects dedtypesX; // empty tiargs // Bugzilla 11553: Check for recursive instantiation of tdx. for (TemplatePrevious *p = tdx->previous; p; p = p->prev) { if (arrayObjectMatch(p->dedargs, &dedtypesX)) { //printf("recursive, no match p->sc=%p %p %s\n", p->sc, this, this->toChars()); /* It must be a subscope of p->sc, other scope chains are not recursive * instantiations. */ for (Scope *scx = sc; scx; scx = scx->enclosing) { if (scx == p->sc) { error(loc, "recursive template expansion while looking for %s.%s", ti->toChars(), tdx->toChars()); goto Lerror; } } } /* BUG: should also check for ref param differences */ } TemplatePrevious pr; pr.prev = tdx->previous; pr.sc = sc; pr.dedargs = &dedtypesX; tdx->previous = ≺ // add this to threaded list fd = resolveFuncCall(loc, sc, s, NULL, tthis, fargs, 1); tdx->previous = pr.prev; // unlink from threaded list } else if (s->isFuncDeclaration()) { fd = resolveFuncCall(loc, sc, s, NULL, tthis, fargs, 1); } else goto Lerror; if (!fd) return 0; if (fd->type->ty != Tfunction) { m->lastf = fd; // to propagate "error match" m->count = 1; m->last = MATCHnomatch; return 1; } Type *tthis_fd = fd->needThis() && !fd->isCtorDeclaration() ? tthis : NULL; TypeFunction *tf = (TypeFunction *)fd->type; MATCH mfa = tf->callMatch(tthis_fd, fargs); if (mfa < m->last) return 0; if (mta < ta_last) goto Ltd_best2; if (mta > ta_last) goto Ltd2; if (mfa < m->last) goto Ltd_best2; if (mfa > m->last) goto Ltd2; //printf("Lambig2\n"); m->nextf = fd; m->count++; return 0; Ltd_best2: return 0; Ltd2: // td is the new best match assert(td->_scope); td_best = td; ti_best = NULL; property = 0; // (backward compatibility) ta_last = mta; m->last = mfa; m->lastf = fd; tthis_best = tthis_fd; ov_index = 0; m->nextf = NULL; m->count = 1; return 0; } //printf("td = %s\n", td->toChars()); for (size_t ovi = 0; f; f = f->overnext0, ovi++) { if (f->type->ty != Tfunction || f->errors) goto Lerror; /* This is a 'dummy' instance to evaluate constraint properly. */ TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); ti->parent = td->parent; // Maybe calculating valid 'enclosing' is unnecessary. FuncDeclaration *fd = f; int x = td->deduceFunctionTemplateMatch(ti, sc, fd, tthis, fargs); MATCH mta = (MATCH)(x >> 4); MATCH mfa = (MATCH)(x & 0xF); //printf("match:t/f = %d/%d\n", mta, mfa); if (!fd || mfa == MATCHnomatch) continue; Type *tthis_fd = fd->needThis() ? tthis : NULL; bool isCtorCall = tthis_fd && fd->isCtorDeclaration(); if (isCtorCall) { // Constructor call requires additional check. TypeFunction *tf = (TypeFunction *)fd->type; assert(tf->next); if (MODimplicitConv(tf->mod, tthis_fd->mod) || (tf->isWild() && tf->isShared() == tthis_fd->isShared()) || fd->isolateReturn()) { tthis_fd = NULL; } else continue; // MATCHnomatch } if (mta < ta_last) goto Ltd_best; if (mta > ta_last) goto Ltd; if (mfa < m->last) goto Ltd_best; if (mfa > m->last) goto Ltd; if (td_best) { // Disambiguate by picking the most specialized TemplateDeclaration MATCH c1 = td->leastAsSpecialized(sc, td_best, fargs); MATCH c2 = td_best->leastAsSpecialized(sc, td, fargs); //printf("1: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; } assert(fd && m->lastf); { // Disambiguate by tf->callMatch TypeFunction *tf1 = (TypeFunction *)fd->type; assert(tf1->ty == Tfunction); TypeFunction *tf2 = (TypeFunction *)m->lastf->type; assert(tf2->ty == Tfunction); MATCH c1 = tf1->callMatch(tthis_fd, fargs); MATCH c2 = tf2->callMatch(tthis_best, fargs); //printf("2: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; } { // Disambiguate by picking the most specialized FunctionDeclaration MATCH c1 = fd->leastAsSpecialized(m->lastf); MATCH c2 = m->lastf->leastAsSpecialized(fd); //printf("3: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; } // Bugzilla 14450: Prefer exact qualified constructor for the creating object type if (isCtorCall && fd->type->mod != m->lastf->type->mod) { if (tthis->mod == fd->type->mod) goto Ltd; if (tthis->mod == m->lastf->type->mod) goto Ltd_best; } m->nextf = fd; m->count++; continue; Ltd_best: // td_best is the best match so far //printf("Ltd_best\n"); continue; Ltd: // td is the new best match //printf("Ltd\n"); assert(td->_scope); td_best = td; ti_best = ti; property = 0; // (backward compatibility) ta_last = mta; m->last = mfa; m->lastf = fd; tthis_best = tthis_fd; ov_index = ovi; m->nextf = NULL; m->count = 1; continue; } return 0; } }; ParamDeduce p; // context p.loc = loc; p.sc = sc; p.tthis = tthis; p.tiargs = tiargs; p.fargs = fargs; // result p.m = m; p.property = 0; p.ov_index = 0; p.td_best = NULL; p.ti_best = NULL; p.ta_last = m->last != MATCHnomatch ? MATCHexact : MATCHnomatch; p.tthis_best = NULL; TemplateDeclaration *td = dstart->isTemplateDeclaration(); if (td && td->funcroot) dstart = td->funcroot; overloadApply(dstart, &p, &ParamDeduce::fp); //printf("td_best = %p, m->lastf = %p\n", p.td_best, m->lastf); if (p.td_best && p.ti_best && m->count == 1) { // Matches to template function assert(p.td_best->onemember && p.td_best->onemember->isFuncDeclaration()); /* The best match is td_best with arguments tdargs. * Now instantiate the template. */ assert(p.td_best->_scope); if (!sc) sc = p.td_best->_scope; // workaround for Type::aliasthisOf TemplateInstance *ti = new TemplateInstance(loc, p.td_best, p.ti_best->tiargs); ti->semantic(sc, fargs); m->lastf = ti->toAlias()->isFuncDeclaration(); if (!m->lastf) goto Lnomatch; if (ti->errors) { Lerror: m->count = 1; assert(m->lastf); m->last = MATCHnomatch; return; } // look forward instantiated overload function // Dsymbol::oneMembers is alredy called in TemplateInstance::semantic. // it has filled overnext0d while (p.ov_index--) { m->lastf = m->lastf->overnext0; assert(m->lastf); } p.tthis_best = m->lastf->needThis() && !m->lastf->isCtorDeclaration() ? tthis : NULL; TypeFunction *tf = (TypeFunction *)m->lastf->type; if (tf->ty == Terror) goto Lerror; assert(tf->ty == Tfunction); if (!tf->callMatch(p.tthis_best, fargs)) goto Lnomatch; /* As Bugzilla 3682 shows, a template instance can be matched while instantiating * that same template. Thus, the function type can be incomplete. Complete it. * * Bugzilla 9208: For auto function, completion should be deferred to the end of * its semantic3. Should not complete it in here. */ if (tf->next && !m->lastf->inferRetType) { m->lastf->type = tf->semantic(loc, sc); } } else if (m->lastf) { // Matches to non template function, // or found matches were ambiguous. assert(m->count >= 1); } else { Lnomatch: m->count = 0; m->lastf = NULL; m->last = MATCHnomatch; } } /************************************************* * Limited function template instantiation for using fd->leastAsSpecialized() */ FuncDeclaration *TemplateDeclaration::doHeaderInstantiation( TemplateInstance *ti, Scope *sc2, FuncDeclaration *fd, Type *tthis, Expressions *fargs) { assert(fd); // function body and contracts are not need if (fd->isCtorDeclaration()) fd = new CtorDeclaration(fd->loc, fd->endloc, fd->storage_class, fd->type->syntaxCopy()); else fd = new FuncDeclaration(fd->loc, fd->endloc, fd->ident, fd->storage_class, fd->type->syntaxCopy()); fd->parent = ti; assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type; tf->fargs = fargs; if (tthis) { // Match 'tthis' to any TemplateThisParameter's bool hasttp = false; for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; TemplateThisParameter *ttp = tp->isTemplateThisParameter(); if (ttp) hasttp = true; } if (hasttp) { tf = (TypeFunction *)tf->addSTC(ModToStc(tthis->mod)); assert(!tf->deco); } } Scope *scx = sc2->push(); // Shouldn't run semantic on default arguments and return type. for (size_t i = 0; i < tf->parameters->dim; i++) (*tf->parameters)[i]->defaultArg = NULL; if (fd->isCtorDeclaration()) { // For constructors, emitting return type is necessary for // isolateReturn() in functionResolve. scx->flags |= SCOPEctor; Dsymbol *parent = toParent2(); Type *tret; AggregateDeclaration *ad = parent->isAggregateDeclaration(); if (!ad || parent->isUnionDeclaration()) { tret = Type::tvoid; } else { tret = ad->handleType(); assert(tret); tret = tret->addStorageClass(fd->storage_class | scx->stc); tret = tret->addMod(tf->mod); } tf->next = tret; if (ad && ad->isStructDeclaration()) tf->isref = 1; //printf("tf = %s\n", tf->toChars()); } else tf->next = NULL; fd->type = tf; fd->type = fd->type->addSTC(scx->stc); fd->type = fd->type->semantic(fd->loc, scx); scx = scx->pop(); if (fd->type->ty != Tfunction) return NULL; fd->originalType = fd->type; // for mangling //printf("\t[%s] fd->type = %s, mod = %x, ", loc.toChars(), fd->type->toChars(), fd->type->mod); //printf("fd->needThis() = %d\n", fd->needThis()); return fd; } bool TemplateDeclaration::hasStaticCtorOrDtor() { return false; // don't scan uninstantiated templates } const char *TemplateDeclaration::toChars() { if (literal) return Dsymbol::toChars(); OutBuffer buf; HdrGenState hgs; buf.writestring(ident->toChars()); buf.writeByte('('); for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; if (i) buf.writestring(", "); ::toCBuffer(tp, &buf, &hgs); } buf.writeByte(')'); if (onemember) { FuncDeclaration *fd = onemember->isFuncDeclaration(); if (fd && fd->type) { TypeFunction *tf = (TypeFunction *)fd->type; buf.writestring(parametersTypeToChars(tf->parameters, tf->varargs)); } } if (constraint) { buf.writestring(" if ("); ::toCBuffer(constraint, &buf, &hgs); buf.writeByte(')'); } return buf.extractString(); } Prot TemplateDeclaration::prot() { return protection; } /**************************************************** * Given a new instance tithis of this TemplateDeclaration, * see if there already exists an instance. * If so, return that existing instance. */ TemplateInstance *TemplateDeclaration::findExistingInstance(TemplateInstance *tithis, Expressions *fargs) { //printf("findExistingInstance(%p)\n", tithis); tithis->fargs = fargs; TemplateInstances *tinstances = (TemplateInstances *)dmd_aaGetRvalue((AA *)instances, (void *)tithis->toHash()); if (tinstances) { for (size_t i = 0; i < tinstances->dim; i++) { TemplateInstance *ti = (*tinstances)[i]; if (tithis->compare(ti) == 0) return ti; } } return NULL; } /******************************************** * Add instance ti to TemplateDeclaration's table of instances. * Return a handle we can use to later remove it if it fails instantiation. */ TemplateInstance *TemplateDeclaration::addInstance(TemplateInstance *ti) { //printf("addInstance() %p %p\n", instances, ti); TemplateInstances **ptinstances = (TemplateInstances **)dmd_aaGet((AA **)&instances, (void *)ti->toHash()); if (!*ptinstances) *ptinstances = new TemplateInstances(); (*ptinstances)->push(ti); return ti; } /******************************************* * Remove TemplateInstance from table of instances. * Input: * handle returned by addInstance() */ void TemplateDeclaration::removeInstance(TemplateInstance *handle) { //printf("removeInstance()\n"); TemplateInstances *tinstances = (TemplateInstances *)dmd_aaGetRvalue((AA *)instances, (void *)handle->toHash()); if (tinstances) { for (size_t i = 0; i < tinstances->dim; i++) { TemplateInstance *ti = (*tinstances)[i]; if (handle == ti) { tinstances->remove(i); break; } } } } /* ======================== Type ============================================ */ /**** * Given an identifier, figure out which TemplateParameter it is. * Return IDX_NOTFOUND if not found. */ static size_t templateIdentifierLookup(Identifier *id, TemplateParameters *parameters) { for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; if (tp->ident->equals(id)) return i; } return IDX_NOTFOUND; } size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters) { if (tparam->ty == Tident) { TypeIdentifier *tident = (TypeIdentifier *)tparam; //printf("\ttident = '%s'\n", tident->toChars()); return templateIdentifierLookup(tident->ident, parameters); } return IDX_NOTFOUND; } unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam) { if ((tparam->mod & MODwild) == 0) return 0; *at = NULL; #define X(U,T) ((U) << 4) | (T) switch (X(tparam->mod, t->mod)) { case X(MODwild, 0): case X(MODwild, MODconst): case X(MODwild, MODshared): case X(MODwild, MODshared | MODconst): case X(MODwild, MODimmutable): case X(MODwildconst, 0): case X(MODwildconst, MODconst): case X(MODwildconst, MODshared): case X(MODwildconst, MODshared | MODconst): case X(MODwildconst, MODimmutable): case X(MODshared | MODwild, MODshared): case X(MODshared | MODwild, MODshared | MODconst): case X(MODshared | MODwild, MODimmutable): case X(MODshared | MODwildconst, MODshared): case X(MODshared | MODwildconst, MODshared | MODconst): case X(MODshared | MODwildconst, MODimmutable): { unsigned char wm = (t->mod & ~MODshared); if (wm == 0) wm = MODmutable; unsigned char m = (t->mod & (MODconst | MODimmutable)) | (tparam->mod & t->mod & MODshared); *at = t->unqualify(m); return wm; } case X(MODwild, MODwild): case X(MODwild, MODwildconst): case X(MODwild, MODshared | MODwild): case X(MODwild, MODshared | MODwildconst): case X(MODwildconst, MODwild): case X(MODwildconst, MODwildconst): case X(MODwildconst, MODshared | MODwild): case X(MODwildconst, MODshared | MODwildconst): case X(MODshared | MODwild, MODshared | MODwild): case X(MODshared | MODwild, MODshared | MODwildconst): case X(MODshared | MODwildconst, MODshared | MODwild): case X(MODshared | MODwildconst, MODshared | MODwildconst): { *at = t->unqualify(tparam->mod & t->mod); return MODwild; } default: return 0; } #undef X } MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam) { // 9*9 == 81 cases #define X(U,T) ((U) << 4) | (T) switch (X(tparam->mod, t->mod)) { case X(0, 0): case X(0, MODconst): case X(0, MODwild): case X(0, MODwildconst): case X(0, MODshared): case X(0, MODshared | MODconst): case X(0, MODshared | MODwild): case X(0, MODshared | MODwildconst): case X(0, MODimmutable): // foo(U) T => T // foo(U) const(T) => const(T) // foo(U) inout(T) => inout(T) // foo(U) inout(const(T)) => inout(const(T)) // foo(U) shared(T) => shared(T) // foo(U) shared(const(T)) => shared(const(T)) // foo(U) shared(inout(T)) => shared(inout(T)) // foo(U) shared(inout(const(T))) => shared(inout(const(T))) // foo(U) immutable(T) => immutable(T) { *at = t; return MATCHexact; } case X(MODconst, MODconst): case X(MODwild, MODwild): case X(MODwildconst, MODwildconst): case X(MODshared, MODshared): case X(MODshared | MODconst, MODshared | MODconst): case X(MODshared | MODwild, MODshared | MODwild): case X(MODshared | MODwildconst, MODshared | MODwildconst): case X(MODimmutable, MODimmutable): // foo(const(U)) const(T) => T // foo(inout(U)) inout(T) => T // foo(inout(const(U))) inout(const(T)) => T // foo(shared(U)) shared(T) => T // foo(shared(const(U))) shared(const(T)) => T // foo(shared(inout(U))) shared(inout(T)) => T // foo(shared(inout(const(U)))) shared(inout(const(T))) => T // foo(immutable(U)) immutable(T) => T { *at = t->mutableOf()->unSharedOf(); return MATCHexact; } case X(MODconst, 0): case X(MODconst, MODwild): case X(MODconst, MODwildconst): case X(MODconst, MODshared | MODconst): case X(MODconst, MODshared | MODwild): case X(MODconst, MODshared | MODwildconst): case X(MODconst, MODimmutable): case X(MODwild, MODshared | MODwild): case X(MODwildconst, MODshared | MODwildconst): case X(MODshared | MODconst, MODimmutable): // foo(const(U)) T => T // foo(const(U)) inout(T) => T // foo(const(U)) inout(const(T)) => T // foo(const(U)) shared(const(T)) => shared(T) // foo(const(U)) shared(inout(T)) => shared(T) // foo(const(U)) shared(inout(const(T))) => shared(T) // foo(const(U)) immutable(T) => T // foo(inout(U)) shared(inout(T)) => shared(T) // foo(inout(const(U))) shared(inout(const(T))) => shared(T) // foo(shared(const(U))) immutable(T) => T { *at = t->mutableOf(); return MATCHconst; } case X(MODconst, MODshared): // foo(const(U)) shared(T) => shared(T) { *at = t; return MATCHconst; } case X(MODshared, MODshared | MODconst): case X(MODshared, MODshared | MODwild): case X(MODshared, MODshared | MODwildconst): case X(MODshared | MODconst, MODshared): // foo(shared(U)) shared(const(T)) => const(T) // foo(shared(U)) shared(inout(T)) => inout(T) // foo(shared(U)) shared(inout(const(T))) => inout(const(T)) // foo(shared(const(U))) shared(T) => T { *at = t->unSharedOf(); return MATCHconst; } case X(MODwildconst, MODimmutable): case X(MODshared | MODconst, MODshared | MODwildconst): case X(MODshared | MODwildconst, MODimmutable): case X(MODshared | MODwildconst, MODshared | MODwild): // foo(inout(const(U))) immutable(T) => T // foo(shared(const(U))) shared(inout(const(T))) => T // foo(shared(inout(const(U)))) immutable(T) => T // foo(shared(inout(const(U)))) shared(inout(T)) => T { *at = t->unSharedOf()->mutableOf(); return MATCHconst; } case X(MODshared | MODconst, MODshared | MODwild): // foo(shared(const(U))) shared(inout(T)) => T { *at = t->unSharedOf()->mutableOf(); return MATCHconst; } case X(MODwild, 0): case X(MODwild, MODconst): case X(MODwild, MODwildconst): case X(MODwild, MODimmutable): case X(MODwild, MODshared): case X(MODwild, MODshared | MODconst): case X(MODwild, MODshared | MODwildconst): case X(MODwildconst, 0): case X(MODwildconst, MODconst): case X(MODwildconst, MODwild): case X(MODwildconst, MODshared): case X(MODwildconst, MODshared | MODconst): case X(MODwildconst, MODshared | MODwild): case X(MODshared, 0): case X(MODshared, MODconst): case X(MODshared, MODwild): case X(MODshared, MODwildconst): case X(MODshared, MODimmutable): case X(MODshared | MODconst, 0): case X(MODshared | MODconst, MODconst): case X(MODshared | MODconst, MODwild): case X(MODshared | MODconst, MODwildconst): case X(MODshared | MODwild, 0): case X(MODshared | MODwild, MODconst): case X(MODshared | MODwild, MODwild): case X(MODshared | MODwild, MODwildconst): case X(MODshared | MODwild, MODimmutable): case X(MODshared | MODwild, MODshared): case X(MODshared | MODwild, MODshared | MODconst): case X(MODshared | MODwild, MODshared | MODwildconst): case X(MODshared | MODwildconst, 0): case X(MODshared | MODwildconst, MODconst): case X(MODshared | MODwildconst, MODwild): case X(MODshared | MODwildconst, MODwildconst): case X(MODshared | MODwildconst, MODshared): case X(MODshared | MODwildconst, MODshared | MODconst): case X(MODimmutable, 0): case X(MODimmutable, MODconst): case X(MODimmutable, MODwild): case X(MODimmutable, MODwildconst): case X(MODimmutable, MODshared): case X(MODimmutable, MODshared | MODconst): case X(MODimmutable, MODshared | MODwild): case X(MODimmutable, MODshared | MODwildconst): // foo(inout(U)) T => nomatch // foo(inout(U)) const(T) => nomatch // foo(inout(U)) inout(const(T)) => nomatch // foo(inout(U)) immutable(T) => nomatch // foo(inout(U)) shared(T) => nomatch // foo(inout(U)) shared(const(T)) => nomatch // foo(inout(U)) shared(inout(const(T))) => nomatch // foo(inout(const(U))) T => nomatch // foo(inout(const(U))) const(T) => nomatch // foo(inout(const(U))) inout(T) => nomatch // foo(inout(const(U))) shared(T) => nomatch // foo(inout(const(U))) shared(const(T)) => nomatch // foo(inout(const(U))) shared(inout(T)) => nomatch // foo(shared(U)) T => nomatch // foo(shared(U)) const(T) => nomatch // foo(shared(U)) inout(T) => nomatch // foo(shared(U)) inout(const(T)) => nomatch // foo(shared(U)) immutable(T) => nomatch // foo(shared(const(U))) T => nomatch // foo(shared(const(U))) const(T) => nomatch // foo(shared(const(U))) inout(T) => nomatch // foo(shared(const(U))) inout(const(T)) => nomatch // foo(shared(inout(U))) T => nomatch // foo(shared(inout(U))) const(T) => nomatch // foo(shared(inout(U))) inout(T) => nomatch // foo(shared(inout(U))) inout(const(T)) => nomatch // foo(shared(inout(U))) immutable(T) => nomatch // foo(shared(inout(U))) shared(T) => nomatch // foo(shared(inout(U))) shared(const(T)) => nomatch // foo(shared(inout(U))) shared(inout(const(T))) => nomatch // foo(shared(inout(const(U)))) T => nomatch // foo(shared(inout(const(U)))) const(T) => nomatch // foo(shared(inout(const(U)))) inout(T) => nomatch // foo(shared(inout(const(U)))) inout(const(T)) => nomatch // foo(shared(inout(const(U)))) shared(T) => nomatch // foo(shared(inout(const(U)))) shared(const(T)) => nomatch // foo(immutable(U)) T => nomatch // foo(immutable(U)) const(T) => nomatch // foo(immutable(U)) inout(T) => nomatch // foo(immutable(U)) inout(const(T)) => nomatch // foo(immutable(U)) shared(T) => nomatch // foo(immutable(U)) shared(const(T)) => nomatch // foo(immutable(U)) shared(inout(T)) => nomatch // foo(immutable(U)) shared(inout(const(T))) => nomatch return MATCHnomatch; default: assert(0); return MATCHnomatch; // silence compiler warning about missing return } #undef X } /* 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 deduceType(RootObject *o, Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm, size_t inferStart) { class DeduceType : public Visitor { public: Scope *sc; Type *tparam; TemplateParameters *parameters; Objects *dedtypes; unsigned *wm; size_t inferStart; MATCH result; DeduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm, size_t inferStart) : sc(sc), tparam(tparam), parameters(parameters), dedtypes(dedtypes), wm(wm), inferStart(inferStart) { result = MATCHnomatch; } void visit(Type *t) { if (!tparam) goto Lnomatch; if (t == tparam) goto Lexact; if (tparam->ty == Tident) { // Determine which parameter tparam is size_t i = templateParameterLookup(tparam, parameters); if (i == IDX_NOTFOUND) { if (!sc) goto Lnomatch; /* Need a loc to go with the semantic routine. */ Loc loc; if (parameters->dim) { TemplateParameter *tp = (*parameters)[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); result = deduceType(t, sc, tparam, parameters, dedtypes, wm); return; } TemplateParameter *tp = (*parameters)[i]; TypeIdentifier *tident = (TypeIdentifier *)tparam; if (tident->idents.dim > 0) { //printf("matching %s to %s\n", tparam->toChars(), t->toChars()); Dsymbol *s = t->toDsymbol(sc); for (size_t j = tident->idents.dim; j-- > 0; ) { RootObject *id = tident->idents[j]; if (id->dyncast() == DYNCAST_IDENTIFIER) { if (!s || !s->parent) goto Lnomatch; Dsymbol *s2 = s->parent->search(Loc(), (Identifier *)id); if (!s2) goto Lnomatch; s2 = s2->toAlias(); //printf("[%d] s = %s %s, s2 = %s %s\n", j, s->kind(), s->toChars(), s2->kind(), s2->toChars()); if (s != s2) { if (Type *tx = s2->getType()) { if (s != tx->toDsymbol(sc)) goto Lnomatch; } else goto Lnomatch; } s = s->parent; } else goto Lnomatch; } //printf("[e] s = %s\n", s?s->toChars():"(null)"); if (tp->isTemplateTypeParameter()) { Type *tt = s->getType(); if (!tt) goto Lnomatch; Type *at = (Type *)(*dedtypes)[i]; if (at && at->ty == Tnone) at = ((TypeDeduced *)at)->tded; if (!at || tt->equals(at)) { (*dedtypes)[i] = tt; goto Lexact; } } if (tp->isTemplateAliasParameter()) { Dsymbol *s2 = (Dsymbol *)(*dedtypes)[i]; if (!s2 || s == s2) { (*dedtypes)[i] = s; goto Lexact; } } goto Lnomatch; } // Found the corresponding parameter tp if (!tp->isTemplateTypeParameter()) goto Lnomatch; Type *at = (Type *)(*dedtypes)[i]; Type *tt; if (unsigned char wx = wm ? deduceWildHelper(t, &tt, tparam) : 0) { // type vs (none) if (!at) { (*dedtypes)[i] = tt; *wm |= wx; result = MATCHconst; return; } // type vs expressions if (at->ty == Tnone) { TypeDeduced *xt = (TypeDeduced *)at; result = xt->matchAll(tt); if (result > MATCHnomatch) { (*dedtypes)[i] = tt; if (result > MATCHconst) result = MATCHconst; // limit level for inout matches delete xt; } return; } // type vs type if (tt->equals(at)) { (*dedtypes)[i] = tt; // Prefer current type match goto Lconst; } if (tt->implicitConvTo(at->constOf())) { (*dedtypes)[i] = at->constOf()->mutableOf(); *wm |= MODconst; goto Lconst; } if (at->implicitConvTo(tt->constOf())) { (*dedtypes)[i] = tt->constOf()->mutableOf(); *wm |= MODconst; goto Lconst; } goto Lnomatch; } else if (MATCH m = deduceTypeHelper(t, &tt, tparam)) { // type vs (none) if (!at) { (*dedtypes)[i] = tt; result = m; return; } // type vs expressions if (at->ty == Tnone) { TypeDeduced *xt = (TypeDeduced *)at; result = xt->matchAll(tt); if (result > MATCHnomatch) { (*dedtypes)[i] = tt; delete xt; } return; } // type vs type if (tt->equals(at)) { goto Lexact; } if (tt->ty == Tclass && at->ty == Tclass) { result = tt->implicitConvTo(at); return; } if (tt->ty == Tsarray && at->ty == Tarray && tt->nextOf()->implicitConvTo(at->nextOf()) >= MATCHconst) { goto Lexact; } } goto Lnomatch; } if (tparam->ty == Ttypeof) { /* Need a loc to go with the semantic routine. */ Loc loc; if (parameters->dim) { TemplateParameter *tp = (*parameters)[0]; loc = tp->loc; } tparam = tparam->semantic(loc, sc); } if (t->ty != tparam->ty) { if (Dsymbol *sym = t->toDsymbol(sc)) { if (sym->isforwardRef() && !tparam->deco) goto Lnomatch; } MATCH m = t->implicitConvTo(tparam); if (m == MATCHnomatch) { if (t->ty == Tclass) { TypeClass *tc = (TypeClass *)t; if (tc->sym->aliasthis && !(tc->att & RECtracingDT)) { tc->att = (AliasThisRec)(tc->att | RECtracingDT); m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm); tc->att = (AliasThisRec)(tc->att & ~RECtracingDT); } } else if (t->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)t; if (ts->sym->aliasthis && !(ts->att & RECtracingDT)) { ts->att = (AliasThisRec)(ts->att | RECtracingDT); m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm); ts->att = (AliasThisRec)(ts->att & ~RECtracingDT); } } } result = m; return; } if (t->nextOf()) { if (tparam->deco && !tparam->hasWild()) { result = t->implicitConvTo(tparam); return; } Type *tpn = tparam->nextOf(); if (wm && t->ty == Taarray && tparam->isWild()) { // Bugzilla 12403: In IFTI, stop inout matching on transitive part of AA types. tpn = tpn->substWildTo(MODmutable); } result = deduceType(t->nextOf(), sc, tpn, parameters, dedtypes, wm); return; } Lexact: result = MATCHexact; return; Lnomatch: result = MATCHnomatch; return; Lconst: result = MATCHconst; } void visit(TypeVector *t) { if (tparam->ty == Tvector) { TypeVector *tp = (TypeVector *)tparam; result = deduceType(t->basetype, sc, tp->basetype, parameters, dedtypes, wm); return; } visit((Type *)t); } void visit(TypeDArray *t) { visit((Type *)t); } void visit(TypeSArray *t) { // Extra check that array dimensions must match if (tparam) { if (tparam->ty == Tarray) { MATCH m = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm); result = (m >= MATCHconst) ? MATCHconvert : MATCHnomatch; return; } TemplateParameter *tp = NULL; Expression *edim = NULL; size_t i; if (tparam->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)tparam; if (tsa->dim->op == TOKvar && ((VarExp *)tsa->dim)->var->storage_class & STCtemplateparameter) { Identifier *id = ((VarExp *)tsa->dim)->var->ident; i = templateIdentifierLookup(id, parameters); assert(i != IDX_NOTFOUND); tp = (*parameters)[i]; } else edim = tsa->dim; } else if (tparam->ty == Taarray) { TypeAArray *taa = (TypeAArray *)tparam; i = templateParameterLookup(taa->index, parameters); if (i != IDX_NOTFOUND) tp = (*parameters)[i]; else { Expression *e; Type *tx; Dsymbol *s; taa->index->resolve(Loc(), sc, &e, &tx, &s); edim = s ? getValue(s) : getValue(e); } } if ((tp && tp->matchArg(sc, t->dim, i, parameters, dedtypes, NULL)) || (edim && edim->toInteger() == t->dim->toInteger())) { result = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm); return; } } visit((Type *)t); return; result = MATCHnomatch; } void visit(TypeAArray *t) { // Extra check that index type must match if (tparam && tparam->ty == Taarray) { TypeAArray *tp = (TypeAArray *)tparam; if (!deduceType(t->index, sc, tp->index, parameters, dedtypes)) { result = MATCHnomatch; return; } } visit((Type *)t); } void visit(TypeFunction *t) { //printf("TypeFunction::deduceType()\n"); //printf("\tthis = %d, ", t->ty); t->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 (t->varargs != tp->varargs || t->linkage != tp->linkage) { result = MATCHnomatch; return; } size_t nfargs = Parameter::dim(t->parameters); size_t nfparams = Parameter::dim(tp->parameters); // bug 2579 fix: Apply function parameter storage classes to parameter types for (size_t i = 0; i < nfparams; i++) { Parameter *fparam = Parameter::getNth(tp->parameters, i); fparam->type = fparam->type->addStorageClass(fparam->storageClass); fparam->storageClass &= ~(STC_TYPECTOR | STCin); } //printf("\t-> this = %d, ", t->ty); t->print(); //printf("\t-> tparam = %d, ", tparam->ty); tparam->print(); /* 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. */ Parameter *fparam = Parameter::getNth(tp->parameters, nfparams - 1); assert(fparam); assert(fparam->type); 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 *tx = (*parameters)[tupi]; TemplateTupleParameter *tup = tx->isTemplateTupleParameter(); if (tup && tup->ident->equals(tid->ident)) break; } /* The types of the function arguments [nfparams - 1 .. nfargs] * now form the tuple argument. */ size_t tuple_dim = nfargs - (nfparams - 1); /* See if existing tuple, and whether it matches or not */ RootObject *o = (*dedtypes)[tupi]; if (o) { // Existing deduced argument must be a tuple, and must match Tuple *tup = isTuple(o); if (!tup || tup->objects.dim != tuple_dim) { result = MATCHnomatch; return; } for (size_t i = 0; i < tuple_dim; i++) { Parameter *arg = Parameter::getNth(t->parameters, nfparams - 1 + i); if (!arg->type->equals(tup->objects[i])) { result = MATCHnomatch; return; } } } else { // Create new tuple Tuple *tup = new Tuple(); tup->objects.setDim(tuple_dim); for (size_t i = 0; i < tuple_dim; i++) { Parameter *arg = Parameter::getNth(t->parameters, nfparams - 1 + i); tup->objects[i] = arg->type; } (*dedtypes)[tupi] = tup; } nfparams--; // don't consider the last parameter for type deduction goto L2; } L1: if (nfargs != nfparams) { result = MATCHnomatch; return; } L2: for (size_t i = 0; i < nfparams; i++) { Parameter *a = Parameter::getNth(t->parameters, i); Parameter *ap = Parameter::getNth(tp->parameters, i); if (!a->isCovariant(t->isref, ap) || !deduceType(a->type, sc, ap->type, parameters, dedtypes)) { result = MATCHnomatch; return; } } } visit((Type *)t); } void visit(TypeIdentifier *t) { // Extra check if (tparam && tparam->ty == Tident) { TypeIdentifier *tp = (TypeIdentifier *)tparam; for (size_t i = 0; i < t->idents.dim; i++) { RootObject *id1 = t->idents[i]; RootObject *id2 = tp->idents[i]; if (!id1->equals(id2)) { result = MATCHnomatch; return; } } } visit((Type *)t); } void visit(TypeInstance *t) { // Extra check if (tparam && tparam->ty == Tinstance && t->tempinst->tempdecl) { TemplateDeclaration *tempdecl = t->tempinst->tempdecl->isTemplateDeclaration(); assert(tempdecl); TypeInstance *tp = (TypeInstance *)tparam; //printf("tempinst->tempdecl = %p\n", tempdecl); //printf("tp->tempinst->tempdecl = %p\n", tp->tempinst->tempdecl); if (!tp->tempinst->tempdecl) { //printf("tp->tempinst->name = '%s'\n", tp->tempinst->name->toChars()); /* Handle case of: * template Foo(T : sa!(T), alias sa) */ size_t i = templateIdentifierLookup(tp->tempinst->name, parameters); if (i == IDX_NOTFOUND) { /* Didn't find it as a parameter identifier. Try looking * it up and seeing if is an alias. See Bugzilla 1454 */ TypeIdentifier *tid = new TypeIdentifier(tp->loc, tp->tempinst->name); Type *tx; Expression *e; Dsymbol *s; tid->resolve(tp->loc, sc, &e, &tx, &s); if (tx) { s = tx->toDsymbol(sc); if (TemplateInstance *ti = s ? s->parent->isTemplateInstance() : NULL) { // Bugzilla 14290: Try to match with ti->tempecl, // only when ti is an enclosing instance. Dsymbol *p = sc->parent; while (p && p != ti) p = p->parent; if (p) s = ti->tempdecl; } } if (s) { s = s->toAlias(); TemplateDeclaration *td = s->isTemplateDeclaration(); if (td) { if (td->overroot) td = td->overroot; for (; td; td = td->overnext) { if (td == tempdecl) goto L2; } } } goto Lnomatch; } TemplateParameter *tpx = (*parameters)[i]; if (!tpx->matchArg(sc, tempdecl, i, parameters, dedtypes, NULL)) goto Lnomatch; } else if (tempdecl != tp->tempinst->tempdecl) goto Lnomatch; L2: for (size_t i = 0; 1; i++) { //printf("\ttest: tempinst->tiargs[%d]\n", i); RootObject *o1 = NULL; if (i < t->tempinst->tiargs->dim) o1 = (*t->tempinst->tiargs)[i]; else if (i < t->tempinst->tdtypes.dim && i < tp->tempinst->tiargs->dim) { // Pick up default arg o1 = t->tempinst->tdtypes[i]; } else if (i >= tp->tempinst->tiargs->dim) break; if (i >= tp->tempinst->tiargs->dim) { size_t dim = tempdecl->parameters->dim - (tempdecl->isVariadic() ? 1 : 0); while (i < dim && ((*tempdecl->parameters)[i]->dependent || (*tempdecl->parameters)[i]->hasDefaultArg())) { i++; } if (i >= dim) break; // match if all remained parameters are dependent goto Lnomatch; } RootObject *o2 = (*tp->tempinst->tiargs)[i]; Type *t2 = isType(o2); size_t j = (t2 && t2->ty == Tident && i == tp->tempinst->tiargs->dim - 1) ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND; if (j != IDX_NOTFOUND && j == parameters->dim - 1 && (*parameters)[j]->isTemplateTupleParameter()) { /* Given: * struct A(B...) {} * alias A!(int, float) X; * static if (is(X Y == A!(Z), Z...)) {} * deduce that Z is a tuple(int, float) */ /* Create tuple from remaining args */ Tuple *vt = new Tuple(); size_t vtdim = (tempdecl->isVariadic() ? t->tempinst->tiargs->dim : t->tempinst->tdtypes.dim) - i; vt->objects.setDim(vtdim); for (size_t k = 0; k < vtdim; k++) { RootObject *o; if (k < t->tempinst->tiargs->dim) o = (*t->tempinst->tiargs)[i + k]; else // Pick up default arg o = t->tempinst->tdtypes[i + k]; vt->objects[k] = o; } Tuple *v = (Tuple *)(*dedtypes)[j]; if (v) { if (!match(v, vt)) goto Lnomatch; } else (*dedtypes)[j] = vt; break; } else if (!o1) break; Type *t1 = isType(o1); Dsymbol *s1 = isDsymbol(o1); Dsymbol *s2 = isDsymbol(o2); Expression *e1 = s1 ? getValue(s1) : getValue(isExpression(o1)); Expression *e2 = isExpression(o2); if (t1 && t2) { if (!deduceType(t1, sc, t2, parameters, dedtypes)) goto Lnomatch; } else if (e1 && e2) { Le: e1 = e1->ctfeInterpret(); /* If it is one of the template parameters for this template, * we should not attempt to interpret it. It already has a value. */ if (e2->op == TOKvar && (((VarExp *)e2)->var->storage_class & STCtemplateparameter)) { /* * (T:Number!(e2), int e2) */ j = templateIdentifierLookup(((VarExp *)e2)->var->ident, parameters); if (j != IDX_NOTFOUND) goto L1; // The template parameter was not from this template // (it may be from a parent template, for example) } e2 = ::semantic(e2, sc); // Bugzilla 13417 e2 = e2->ctfeInterpret(); //printf("e1 = %s, type = %s %d\n", e1->toChars(), e1->type->toChars(), e1->type->ty); //printf("e2 = %s, type = %s %d\n", e2->toChars(), e2->type->toChars(), e2->type->ty); if (!e1->equals(e2)) { if (!e2->implicitConvTo(e1->type)) goto Lnomatch; e2 = e2->implicitCastTo(sc, e1->type); e2 = e2->ctfeInterpret(); if (!e1->equals(e2)) goto Lnomatch; } } else if (e1 && t2 && t2->ty == Tident) { j = templateParameterLookup(t2, parameters); L1: if (j == IDX_NOTFOUND) { t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2); if (e2) goto Le; goto Lnomatch; } if (!(*parameters)[j]->matchArg(sc, e1, j, parameters, dedtypes, NULL)) goto Lnomatch; } else if (s1 && s2) { Ls: if (!s1->equals(s2)) goto Lnomatch; } else if (s1 && t2 && t2->ty == Tident) { j = templateParameterLookup(t2, parameters); if (j == IDX_NOTFOUND) { t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2); if (s2) goto Ls; goto Lnomatch; } if (!(*parameters)[j]->matchArg(sc, s1, j, parameters, dedtypes, NULL)) goto Lnomatch; } else goto Lnomatch; } } visit((Type *)t); return; Lnomatch: //printf("no match\n"); result = MATCHnomatch; } void visit(TypeStruct *t) { /* 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 = t->sym->parent->isTemplateInstance(); if (tparam && tparam->ty == Tinstance) { if (ti && ti->toAlias() == t->sym) { TypeInstance *tx = new TypeInstance(Loc(), ti); result = deduceType(tx, sc, tparam, parameters, dedtypes, wm); return; } /* Match things like: * S!(T).foo */ TypeInstance *tpi = (TypeInstance *)tparam; if (tpi->idents.dim) { RootObject *id = tpi->idents[tpi->idents.dim - 1]; if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id)) { Type *tparent = t->sym->parent->getType(); if (tparent) { /* Slice off the .foo in S!(T).foo */ tpi->idents.dim--; result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm); tpi->idents.dim++; return; } } } } // Extra check if (tparam && tparam->ty == Tstruct) { TypeStruct *tp = (TypeStruct *)tparam; //printf("\t%d\n", (MATCH) t->implicitConvTo(tp)); if (wm && t->deduceWild(tparam, false)) { result = MATCHconst; return; } result = t->implicitConvTo(tp); return; } visit((Type *)t); } void visit(TypeEnum *t) { // Extra check if (tparam && tparam->ty == Tenum) { TypeEnum *tp = (TypeEnum *)tparam; if (t->sym == tp->sym) visit((Type *)t); else result = MATCHnomatch; return; } Type *tb = t->toBasetype(); if (tb->ty == tparam->ty || (tb->ty == Tsarray && tparam->ty == Taarray)) { result = deduceType(tb, sc, tparam, parameters, dedtypes, wm); return; } visit((Type *)t); } /* Helper for TypeClass::deduceType(). * Classes can match with implicit conversion to a base class or interface. * This is complicated, because there may be more than one base class which * matches. In such cases, one or more parameters remain ambiguous. * For example, * * interface I(X, Y) {} * class C : I(uint, double), I(char, double) {} * C x; * foo(T, U)( I!(T, U) x) * * deduces that U is double, but T remains ambiguous (could be char or uint). * * Given a baseclass b, and initial deduced types 'dedtypes', this function * tries to match tparam with b, and also tries all base interfaces of b. * If a match occurs, numBaseClassMatches is incremented, and the new deduced * types are ANDed with the current 'best' estimate for dedtypes. */ static void deduceBaseClassParameters(BaseClass *b, Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, Objects *best, int &numBaseClassMatches) { TemplateInstance *parti = b->sym ? b->sym->parent->isTemplateInstance() : NULL; if (parti) { // Make a temporary copy of dedtypes so we don't destroy it Objects *tmpdedtypes = new Objects(); tmpdedtypes->setDim(dedtypes->dim); memcpy(tmpdedtypes->tdata(), dedtypes->tdata(), dedtypes->dim * sizeof(void *)); TypeInstance *t = new TypeInstance(Loc(), parti); MATCH m = deduceType(t, sc, tparam, parameters, tmpdedtypes); if (m > MATCHnomatch) { // If this is the first ever match, it becomes our best estimate if (numBaseClassMatches==0) memcpy(best->tdata(), tmpdedtypes->tdata(), tmpdedtypes->dim * sizeof(void *)); else for (size_t k = 0; k < tmpdedtypes->dim; ++k) { // If we've found more than one possible type for a parameter, // mark it as unknown. if ((*tmpdedtypes)[k] != (*best)[k]) (*best)[k] = (*dedtypes)[k]; } ++numBaseClassMatches; } } // Now recursively test the inherited interfaces for (size_t j = 0; j < b->baseInterfaces.length; ++j) { BaseClass *bi = &b->baseInterfaces.ptr[j]; deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches); } } void visit(TypeClass *t) { //printf("TypeClass::deduceType(this = %s)\n", t->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 = t->sym->parent->isTemplateInstance(); if (tparam && tparam->ty == Tinstance) { if (ti && ti->toAlias() == t->sym) { TypeInstance *tx = new TypeInstance(Loc(), ti); MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm); // Even if the match fails, there is still a chance it could match // a base class. if (m != MATCHnomatch) { result = m; return; } } /* Match things like: * S!(T).foo */ TypeInstance *tpi = (TypeInstance *)tparam; if (tpi->idents.dim) { RootObject *id = tpi->idents[tpi->idents.dim - 1]; if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id)) { Type *tparent = t->sym->parent->getType(); if (tparent) { /* Slice off the .foo in S!(T).foo */ tpi->idents.dim--; result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm); tpi->idents.dim++; return; } } } // If it matches exactly or via implicit conversion, we're done visit((Type *)t); if (result != MATCHnomatch) return; /* There is still a chance to match via implicit conversion to * a base class or interface. Because there could be more than one such * match, we need to check them all. */ int numBaseClassMatches = 0; // Have we found an interface match? // Our best guess at dedtypes Objects *best = new Objects(); best->setDim(dedtypes->dim); ClassDeclaration *s = t->sym; while (s && s->baseclasses->dim > 0) { // Test the base class deduceBaseClassParameters((*s->baseclasses)[0], sc, tparam, parameters, dedtypes, best, numBaseClassMatches); // Test the interfaces inherited by the base class for (size_t i = 0; i < s->interfaces.length; ++i) { BaseClass *b = s->interfaces.ptr[i]; deduceBaseClassParameters(b, sc, tparam, parameters, dedtypes, best, numBaseClassMatches); } s = (*s->baseclasses)[0]->sym; } if (numBaseClassMatches == 0) { result = MATCHnomatch; return; } // If we got at least one match, copy the known types into dedtypes memcpy(dedtypes->tdata(), best->tdata(), best->dim * sizeof(void *)); result = MATCHconvert; return; } // Extra check if (tparam && tparam->ty == Tclass) { TypeClass *tp = (TypeClass *)tparam; //printf("\t%d\n", (MATCH) t->implicitConvTo(tp)); if (wm && t->deduceWild(tparam, false)) { result = MATCHconst; return; } result = t->implicitConvTo(tp); return; } visit((Type *)t); } void visit(Expression *e) { //printf("Expression::deduceType(e = %s)\n", e->toChars()); size_t i = templateParameterLookup(tparam, parameters); if (i == IDX_NOTFOUND || ((TypeIdentifier *)tparam)->idents.dim > 0) { if (e == emptyArrayElement && tparam->ty == Tarray) { Type *tn = ((TypeNext *)tparam)->next; result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); return; } e->type->accept(this); return; } TemplateTypeParameter *tp = (*parameters)[i]->isTemplateTypeParameter(); if (!tp) return; // nomatch if (e == emptyArrayElement) { if ((*dedtypes)[i]) { result = MATCHexact; return; } if (tp->defaultType) { tp->defaultType->accept(this); return; } } Type *at = (Type *)(*dedtypes)[i]; Type *tt; if (unsigned char wx = deduceWildHelper(e->type, &tt, tparam)) { *wm |= wx; result = MATCHconst; } else if (MATCH m = deduceTypeHelper(e->type, &tt, tparam)) { result = m; } else return; // nomatch // expression vs (none) if (!at) { (*dedtypes)[i] = new TypeDeduced(tt, e, tparam); return; } TypeDeduced *xt = NULL; if (at->ty == Tnone) { xt = (TypeDeduced *)at; at = xt->tded; } // From previous matched expressions to current deduced type MATCH match1 = xt ? xt->matchAll(tt) : MATCHnomatch; // From current expresssion to previous deduced type Type *pt = at->addMod(tparam->mod); if (*wm) pt = pt->substWildTo(*wm); MATCH match2 = e->implicitConvTo(pt); if (match1 > MATCHnomatch && match2 > MATCHnomatch) { if (at->implicitConvTo(tt) <= MATCHnomatch) match1 = MATCHnomatch; // Prefer at else if (tt->implicitConvTo(at) <= MATCHnomatch) match2 = MATCHnomatch; // Prefer tt else if (tt->isTypeBasic() && tt->ty == at->ty && tt->mod != at->mod) { if (!tt->isMutable() && !at->isMutable()) tt = tt->mutableOf()->addMod(MODmerge(tt->mod, at->mod)); else if (tt->isMutable()) { if (at->mod == 0) // Prefer unshared match1 = MATCHnomatch; else match2 = MATCHnomatch; } else if (at->isMutable()) { if (tt->mod == 0) // Prefer unshared match2 = MATCHnomatch; else match1 = MATCHnomatch; } //printf("tt = %s, at = %s\n", tt->toChars(), at->toChars()); } else { match1 = MATCHnomatch; match2 = MATCHnomatch; } } if (match1 > MATCHnomatch) { // Prefer current match: tt if (xt) xt->update(tt, e, tparam); else (*dedtypes)[i] = tt; result = match1; return; } if (match2 > MATCHnomatch) { // Prefer previous match: (*dedtypes)[i] if (xt) xt->update(e, tparam); result = match2; return; } /* Deduce common type */ if (Type *t = rawTypeMerge(at, tt)) { if (xt) xt->update(t, e, tparam); else (*dedtypes)[i] = t; pt = tt->addMod(tparam->mod); if (*wm) pt = pt->substWildTo(*wm); result = e->implicitConvTo(pt); return; } result = MATCHnomatch; } MATCH deduceEmptyArrayElement() { if (!emptyArrayElement) { emptyArrayElement = new IdentifierExp(Loc(), Id::p); // dummy emptyArrayElement->type = Type::tvoid; } assert(tparam->ty == Tarray); Type *tn = ((TypeNext *)tparam)->next; return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); } void visit(NullExp *e) { if (tparam->ty == Tarray && e->type->ty == Tnull) { // tparam:T[] <- e:null (void[]) result = deduceEmptyArrayElement(); return; } visit((Expression *)e); } void visit(StringExp *e) { Type *taai; if (e->type->ty == Tarray && (tparam->ty == Tsarray || (tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident && ((TypeIdentifier *)taai)->idents.dim == 0))) { // Consider compile-time known boundaries e->type->nextOf()->sarrayOf(e->len)->accept(this); return; } visit((Expression *)e); } void visit(ArrayLiteralExp *e) { // https://issues.dlang.org/show_bug.cgi?id=20092 if (e->elements && e->elements->dim && e->type->toBasetype()->nextOf()->ty == Tvoid) { result = deduceEmptyArrayElement(); return; } if ((!e->elements || !e->elements->dim) && e->type->toBasetype()->nextOf()->ty == Tvoid && tparam->ty == Tarray) { // tparam:T[] <- e:[] (void[]) result = deduceEmptyArrayElement(); return; } if (tparam->ty == Tarray && e->elements && e->elements->dim) { Type *tn = ((TypeDArray *)tparam)->next; result = MATCHexact; if (e->basis) { MATCH m = deduceType(e->basis, sc, tn, parameters, dedtypes, wm); if (m < result) result = m; } for (size_t i = 0; i < e->elements->dim; i++) { if (result <= MATCHnomatch) break; Expression *el = (*e->elements)[i]; if (!el) continue; MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm); if (m < result) result = m; } return; } Type *taai; if (e->type->ty == Tarray && (tparam->ty == Tsarray || (tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident && ((TypeIdentifier *)taai)->idents.dim == 0))) { // Consider compile-time known boundaries e->type->nextOf()->sarrayOf(e->elements->dim)->accept(this); return; } visit((Expression *)e); } void visit(AssocArrayLiteralExp *e) { if (tparam->ty == Taarray && e->keys && e->keys->dim) { TypeAArray *taa = (TypeAArray *)tparam; result = MATCHexact; for (size_t i = 0; i < e->keys->dim; i++) { MATCH m1 = deduceType((*e->keys)[i], sc, taa->index, parameters, dedtypes, wm); if (m1 < result) result = m1; if (result <= MATCHnomatch) break; MATCH m2 = deduceType((*e->values)[i], sc, taa->next, parameters, dedtypes, wm); if (m2 < result) result = m2; if (result <= MATCHnomatch) break; } return; } visit((Expression *)e); } void visit(FuncExp *e) { //printf("e->type = %s, tparam = %s\n", e->type->toChars(), tparam->toChars()); if (e->td) { Type *to = tparam; if (!to->nextOf() || to->nextOf()->ty != Tfunction) return; TypeFunction *tof = (TypeFunction *)to->nextOf(); // Parameter types inference from 'tof' assert(e->td->_scope); TypeFunction *tf = (TypeFunction *)e->fd->type; //printf("\ttof = %s\n", tof->toChars()); //printf("\ttf = %s\n", tf->toChars()); size_t dim = Parameter::dim(tf->parameters); if (Parameter::dim(tof->parameters) != dim || tof->varargs != tf->varargs) return; Objects *tiargs = new Objects(); tiargs->reserve(e->td->parameters->dim); for (size_t i = 0; i < e->td->parameters->dim; i++) { TemplateParameter *tp = (*e->td->parameters)[i]; size_t u = 0; for (; u < dim; u++) { Parameter *p = Parameter::getNth(tf->parameters, u); if (p->type->ty == Tident && ((TypeIdentifier *)p->type)->ident == tp->ident) { break; } } assert(u < dim); Parameter *pto = Parameter::getNth(tof->parameters, u); if (!pto) break; Type *t = pto->type->syntaxCopy(); // Bugzilla 11774 if (reliesOnTident(t, parameters, inferStart)) return; t = t->semantic(e->loc, sc); if (t->ty == Terror) return; tiargs->push(t); } // Set target of return type inference if (!tf->next && tof->next) e->fd->treq = tparam; TemplateInstance *ti = new TemplateInstance(e->loc, e->td, tiargs); Expression *ex = new ScopeExp(e->loc, ti); ex = ::semantic(ex, e->td->_scope); // Reset inference target for the later re-semantic e->fd->treq = NULL; if (ex->op == TOKerror) return; if (ex->op != TOKfunction) return; visit(ex->type); return; } Type *t = e->type; if (t->ty == Tdelegate && tparam->ty == Tpointer) return; // Allow conversion from implicit function pointer to delegate if (e->tok == TOKreserved && t->ty == Tpointer && tparam->ty == Tdelegate) { TypeFunction *tf = (TypeFunction *)t->nextOf(); t = (new TypeDelegate(tf))->merge(); } //printf("tparam = %s <= e->type = %s, t = %s\n", tparam->toChars(), e->type->toChars(), t->toChars()); visit(t); } void visit(SliceExp *e) { Type *taai; if (e->type->ty == Tarray && (tparam->ty == Tsarray || (tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident && ((TypeIdentifier *)taai)->idents.dim == 0))) { // Consider compile-time known boundaries if (Type *tsa = toStaticArrayType(e)) { tsa->accept(this); return; } } visit((Expression *)e); } void visit(CommaExp *e) { ((CommaExp *)e)->e2->accept(this); } }; DeduceType v(sc, tparam, parameters, dedtypes, wm, inferStart); if (Type *t = isType(o)) t->accept(&v); else { assert(isExpression(o) && wm); ((Expression *)o)->accept(&v); } return v.result; } /******************************* * Input: * t Tested type, if NULL, returns NULL. * tparams Optional template parameters. * == NULL: * If one of the subtypes of this type is a TypeIdentifier, * i.e. it's an unresolved type, return that type. * != NULL: * Only when the TypeIdentifier is one of template parameters, * return that type. */ bool reliesOnTident(Type *t, TemplateParameters *tparams, size_t iStart) { class ReliesOnTident : public Visitor { public: TemplateParameters *tparams; size_t iStart; bool result; ReliesOnTident(TemplateParameters *tparams, size_t iStart) : tparams(tparams), iStart(iStart) { result = false; } void visit(Type *) { } void visit(TypeNext *t) { t->next->accept(this); } void visit(TypeVector *t) { t->basetype->accept(this); } void visit(TypeAArray *t) { visit((TypeNext *)t); if (!result) t->index->accept(this); } void visit(TypeFunction *t) { size_t dim = Parameter::dim(t->parameters); for (size_t i = 0; i < dim; i++) { Parameter *fparam = Parameter::getNth(t->parameters, i); fparam->type->accept(this); if (result) return; } if (t->next) t->next->accept(this); } void visit(TypeIdentifier *t) { if (!tparams) { result = true; return; } for (size_t i = iStart; i < tparams->dim; i++) { TemplateParameter *tp = (*tparams)[i]; if (tp->ident->equals(t->ident)) { result = true; return; } } } void visit(TypeInstance *t) { if (!tparams) return; for (size_t i = iStart; i < tparams->dim; i++) { TemplateParameter *tp = (*tparams)[i]; if (t->tempinst->name == tp->ident) { result = true; return; } } if (!t->tempinst->tiargs) return; for (size_t i = 0; i < t->tempinst->tiargs->dim; i++) { Type *ta = isType((*t->tempinst->tiargs)[i]); if (ta) { ta->accept(this); if (result) return; } } } void visit(TypeTypeof *t) { //printf("TypeTypeof::reliesOnTident('%s')\n", t->toChars()); t->exp->accept(this); } void visit(TypeTuple *t) { if (t->arguments) { for (size_t i = 0; i < t->arguments->dim; i++) { Parameter *arg = (*t->arguments)[i]; arg->type->accept(this); if (result) return; } } } void visit(Expression *) { //printf("Expression::reliesOnTident('%s')\n", e->toChars()); } void visit(IdentifierExp *e) { //printf("IdentifierExp::reliesOnTident('%s')\n", e->toChars()); for (size_t i = iStart; i < tparams->dim; i++) { TemplateParameter *tp = (*tparams)[i]; if (e->ident == tp->ident) { result = true; return; } } } void visit(TupleExp *e) { //printf("TupleExp::reliesOnTident('%s')\n", e->toChars()); if (e->exps) { for (size_t i = 0; i < e->exps->dim; i++) { Expression *ea = (*e->exps)[i]; ea->accept(this); if (result) return; } } } void visit(ArrayLiteralExp *e) { //printf("ArrayLiteralExp::reliesOnTident('%s')\n", e->toChars()); if (e->elements) { for (size_t i = 0; i < e->elements->dim; i++) { Expression *el = (*e->elements)[i]; el->accept(this); if (result) return; } } } void visit(AssocArrayLiteralExp *e) { //printf("AssocArrayLiteralExp::reliesOnTident('%s')\n", e->toChars()); for (size_t i = 0; i < e->keys->dim; i++) { Expression *ek = (*e->keys)[i]; ek->accept(this); if (result) return; } for (size_t i = 0; i < e->values->dim; i++) { Expression *ev = (*e->values)[i]; ev->accept(this); if (result) return; } } void visit(StructLiteralExp *e) { //printf("StructLiteralExp::reliesOnTident('%s')\n", e->toChars()); if (e->elements) { for (size_t i = 0; i < e->elements->dim; i++) { Expression *ea = (*e->elements)[i]; ea->accept(this); if (result) return; } } } void visit(TypeExp *e) { //printf("TypeExp::reliesOnTident('%s')\n", e->toChars()); e->type->accept(this); } void visit(NewExp *e) { //printf("NewExp::reliesOnTident('%s')\n", e->toChars()); if (e->thisexp) e->thisexp->accept(this); if (!result && e->newargs) { for (size_t i = 0; i < e->newargs->dim; i++) { Expression *ea = (*e->newargs)[i]; ea->accept(this); if (result) return; } } e->newtype->accept(this); if (!result && e->arguments) { for (size_t i = 0; i < e->arguments->dim; i++) { Expression *ea = (*e->arguments)[i]; ea->accept(this); if (result) return; } } } void visit(NewAnonClassExp *) { //printf("NewAnonClassExp::reliesOnTident('%s')\n", e->toChars()); result = true; } void visit(FuncExp *) { //printf("FuncExp::reliesOnTident('%s')\n", e->toChars()); result = true; } void visit(TypeidExp *e) { //printf("TypeidExp::reliesOnTident('%s')\n", e->toChars()); if (Expression *ea = isExpression(e->obj)) ea->accept(this); else if (Type *ta = isType(e->obj)) ta->accept(this); } void visit(TraitsExp *e) { //printf("TraitsExp::reliesOnTident('%s')\n", e->toChars()); if (e->args) { for (size_t i = 0; i < e->args->dim; i++) { RootObject *oa = (*e->args)[i]; if (Expression *ea = isExpression(oa)) ea->accept(this); else if (Type *ta = isType(oa)) ta->accept(this); if (result) return; } } } void visit(IsExp *e) { //printf("IsExp::reliesOnTident('%s')\n", e->toChars()); e->targ->accept(this); } void visit(UnaExp *e) { //printf("UnaExp::reliesOnTident('%s')\n", e->toChars()); e->e1->accept(this); } void visit(DotTemplateInstanceExp *e) { //printf("DotTemplateInstanceExp::reliesOnTident('%s')\n", e->toChars()); visit((UnaExp *)e); if (!result && e->ti->tiargs) { for (size_t i = 0; i < e->ti->tiargs->dim; i++) { RootObject *oa = (*e->ti->tiargs)[i]; if (Expression *ea = isExpression(oa)) ea->accept(this); else if (Type *ta = isType(oa)) ta->accept(this); if (result) return; } } } void visit(CallExp *e) { //printf("CallExp::reliesOnTident('%s')\n", e->toChars()); visit((UnaExp *)e); if (!result && e->arguments) { for (size_t i = 0; i < e->arguments->dim; i++) { Expression *ea = (*e->arguments)[i]; ea->accept(this); if (result) return; } } } void visit(CastExp *e) { //printf("CastExp::reliesOnTident('%s')\n", e->toChars()); visit((UnaExp *)e); // e.to can be null for cast() with no type if (!result && e->to) e->to->accept(this); } void visit(SliceExp *e) { //printf("SliceExp::reliesOnTident('%s')\n", e->toChars()); visit((UnaExp *)e); if (!result && e->lwr) e->lwr->accept(this); if (!result && e->upr) e->upr->accept(this); } void visit(IntervalExp *e) { //printf("IntervalExp::reliesOnTident('%s')\n", e->toChars()); e->lwr->accept(this); if (!result) e->upr->accept(this); } void visit(ArrayExp *e) { //printf("ArrayExp::reliesOnTident('%s')\n", e->toChars()); visit((UnaExp *)e); if (!result && e->arguments) { for (size_t i = 0; i < e->arguments->dim; i++) { Expression *ea = (*e->arguments)[i]; ea->accept(this); } } } void visit(BinExp *e) { //printf("BinExp::reliesOnTident('%s')\n", e->toChars()); e->e1->accept(this); if (!result) e->e2->accept(this); } void visit(CondExp *e) { //printf("BinExp::reliesOnTident('%s')\n", e->toChars()); e->econd->accept(this); if (!result) visit((BinExp *)e); } }; if (!t) return false; ReliesOnTident v(tparams, iStart); t->accept(&v); return v.result; } /* ======================== TemplateParameter =============================== */ TemplateParameter::TemplateParameter(Loc loc, Identifier *ident) { this->loc = loc; this->ident = ident; this->dependent = false; } TemplateTypeParameter *TemplateParameter::isTemplateTypeParameter() { return NULL; } TemplateValueParameter *TemplateParameter::isTemplateValueParameter() { return NULL; } TemplateAliasParameter *TemplateParameter::isTemplateAliasParameter() { return NULL; } TemplateTupleParameter *TemplateParameter::isTemplateTupleParameter() { return NULL; } TemplateThisParameter *TemplateParameter::isTemplateThisParameter() { return NULL; } /******************************************* * Match to a particular TemplateParameter. * Input: * instLoc location that the template is instantiated. * tiargs[] actual arguments to template instance * i i'th argument * parameters[] template parameters * dedtypes[] deduced arguments to template instance * *psparam set to symbol declared and initialized to dedtypes[i] */ MATCH TemplateParameter::matchArg(Loc instLoc, Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam) { RootObject *oarg; if (i < tiargs->dim) oarg = (*tiargs)[i]; else { // Get default argument instead oarg = defaultArg(instLoc, sc); if (!oarg) { assert(i < dedtypes->dim); // It might have already been deduced oarg = (*dedtypes)[i]; if (!oarg) goto Lnomatch; } } return matchArg(sc, oarg, i, parameters, dedtypes, psparam); Lnomatch: if (psparam) *psparam = NULL; return MATCHnomatch; } /* ======================== TemplateTypeParameter =========================== */ // type-parameter Type *TemplateTypeParameter::tdummy = NULL; 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() { return new TemplateTypeParameter(loc, ident, specType ? specType->syntaxCopy() : NULL, defaultType ? defaultType->syntaxCopy() : NULL); } bool TemplateTypeParameter::declareParameter(Scope *sc) { //printf("TemplateTypeParameter::declareParameter('%s')\n", ident->toChars()); TypeIdentifier *ti = new TypeIdentifier(loc, ident); Declaration *ad = new AliasDeclaration(loc, ident, ti); return sc->insert(ad) != NULL; } bool TemplateTypeParameter::semantic(Scope *sc, TemplateParameters *parameters) { //printf("TemplateTypeParameter::semantic('%s')\n", ident->toChars()); if (specType && !reliesOnTident(specType, parameters)) { specType = specType->semantic(loc, sc); } return !(specType && isError(specType)); } MATCH TemplateTypeParameter::matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam) { //printf("TemplateTypeParameter::matchArg('%s')\n", ident->toChars()); MATCH m = MATCHexact; Type *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()); if (specType) { if (!ta || ta == tdummy) goto Lnomatch; //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta->toChars(), specType->toChars()); MATCH m2 = deduceType(ta, sc, specType, parameters, dedtypes); if (m2 <= MATCHnomatch) { //printf("\tfailed deduceType\n"); goto Lnomatch; } if (m2 < m) m = m2; if ((*dedtypes)[i]) { Type *t = (Type *)(*dedtypes)[i]; if (dependent && !t->equals(ta)) // Bugzilla 14357 goto Lnomatch; /* This is a self-dependent parameter. For example: * template X(T : T*) {} * template X(T : S!T, alias S) {} */ //printf("t = %s ta = %s\n", t->toChars(), ta->toChars()); ta = t; } } else { if ((*dedtypes)[i]) { // Must match already deduced type Type *t = (Type *)(*dedtypes)[i]; if (!t->equals(ta)) { //printf("t = %s ta = %s\n", t->toChars(), ta->toChars()); goto Lnomatch; } } else { // So that matches with specializations are better m = MATCHconvert; } } (*dedtypes)[i] = ta; if (psparam) *psparam = new AliasDeclaration(loc, ident, ta); //printf("\tm = %d\n", m); return dependent ? MATCHexact : m; Lnomatch: if (psparam) *psparam = NULL; //printf("\tm = %d\n", MATCHnomatch); return MATCHnomatch; } void TemplateTypeParameter::print(RootObject *oarg, RootObject *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("\tParameter: %s\n", t ? t->toChars() : "NULL"); printf("\tDeduced Type: %s\n", ta->toChars()); } void *TemplateTypeParameter::dummyArg() { Type *t = specType; if (!t) { // Use this for alias-parameter's too (?) if (!tdummy) tdummy = new TypeIdentifier(loc, ident); t = tdummy; } return (void *)t; } RootObject *TemplateTypeParameter::specialization() { return specType; } RootObject *TemplateTypeParameter::defaultArg(Loc, Scope *sc) { Type *t = defaultType; if (t) { t = t->syntaxCopy(); t = t->semantic(loc, sc); // use the parameter loc } return t; } bool TemplateTypeParameter::hasDefaultArg() { return defaultType != NULL; } /* ======================== TemplateThisParameter =========================== */ // 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() { return new TemplateThisParameter(loc, ident, specType ? specType->syntaxCopy() : NULL, defaultType ? defaultType->syntaxCopy() : NULL); } /* ======================== TemplateAliasParameter ========================== */ // alias-parameter Dsymbol *TemplateAliasParameter::sdummy = NULL; TemplateAliasParameter::TemplateAliasParameter(Loc loc, Identifier *ident, Type *specType, RootObject *specAlias, RootObject *defaultAlias) : TemplateParameter(loc, ident) { this->ident = ident; this->specType = specType; this->specAlias = specAlias; this->defaultAlias = defaultAlias; } TemplateAliasParameter *TemplateAliasParameter::isTemplateAliasParameter() { return this; } TemplateParameter *TemplateAliasParameter::syntaxCopy() { return new TemplateAliasParameter(loc, ident, specType ? specType->syntaxCopy() : NULL, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias)); } bool TemplateAliasParameter::declareParameter(Scope *sc) { TypeIdentifier *ti = new TypeIdentifier(loc, ident); Declaration *ad = new AliasDeclaration(loc, ident, ti); return sc->insert(ad) != NULL; } static RootObject *aliasParameterSemantic(Loc loc, Scope *sc, RootObject *o, TemplateParameters *parameters) { if (o) { Expression *ea = isExpression(o); Type *ta = isType(o); if (ta && (!parameters || !reliesOnTident(ta, parameters))) { Dsymbol *s = ta->toDsymbol(sc); if (s) o = s; else o = ta->semantic(loc, sc); } else if (ea) { sc = sc->startCTFE(); ea = ::semantic(ea, sc); sc = sc->endCTFE(); o = ea->ctfeInterpret(); } } return o; } bool TemplateAliasParameter::semantic(Scope *sc, TemplateParameters *parameters) { if (specType && !reliesOnTident(specType, parameters)) { specType = specType->semantic(loc, sc); } specAlias = aliasParameterSemantic(loc, sc, specAlias, parameters); return !(specType && isError(specType)) && !(specAlias && isError(specAlias)); } MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam) { //printf("TemplateAliasParameter::matchArg('%s')\n", ident->toChars()); MATCH m = MATCHexact; Type *ta = isType(oarg); RootObject *sa = ta && !ta->deco ? NULL : getDsymbol(oarg); Expression *ea = isExpression(oarg); if (ea && (ea->op == TOKthis || ea->op == TOKsuper)) sa = ((ThisExp *)ea)->var; else if (ea && ea->op == TOKscope) sa = ((ScopeExp *)ea)->sds; if (sa) { if (((Dsymbol *)sa)->isAggregateDeclaration()) m = MATCHconvert; /* 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; if (ea) { if (specType) { if (!ea->type->equals(specType)) goto Lnomatch; } } else if (ta && ta->ty == Tinstance && !specAlias) { /* Bugzilla xxxxx: Specialized parameter should be prefeerd * match to the template type parameter. * template X(alias a) {} // a == this * template X(alias a : B!A, alias B, A...) {} // B!A => ta */ } else if (sa && sa == TemplateTypeParameter::tdummy) { /* Bugzilla 2025: Aggregate Types should preferentially * match to the template type parameter. * template X(alias a) {} // a == this * template X(T) {} // T => sa */ } else goto Lnomatch; } if (specAlias) { if (sa == sdummy) goto Lnomatch; Dsymbol *sx = isDsymbol(sa); if (sa != specAlias && sx) { Type *talias = isType(specAlias); if (!talias) goto Lnomatch; TemplateInstance *ti = sx->isTemplateInstance(); if (!ti && sx->parent) { ti = sx->parent->isTemplateInstance(); if (ti && ti->name != sx->ident) goto Lnomatch; } if (!ti) goto Lnomatch; Type *t = new TypeInstance(Loc(), ti); MATCH m2 = deduceType(t, sc, talias, parameters, dedtypes); if (m2 <= MATCHnomatch) goto Lnomatch; } } else if ((*dedtypes)[i]) { // Must match already deduced symbol RootObject *si = (*dedtypes)[i]; if (!sa || si != sa) goto Lnomatch; } (*dedtypes)[i] = sa; if (psparam) { if (Dsymbol *s = isDsymbol(sa)) { *psparam = new AliasDeclaration(loc, ident, s); } else if (Type *t = isType(sa)) { *psparam = new AliasDeclaration(loc, ident, t); } 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 dependent ? MATCHexact : m; Lnomatch: if (psparam) *psparam = NULL; //printf("\tm = %d\n", MATCHnomatch); return MATCHnomatch; } void TemplateAliasParameter::print(RootObject *, RootObject *oded) { printf(" %s\n", ident->toChars()); Dsymbol *sa = isDsymbol(oded); assert(sa); printf("\tParameter alias: %s\n", sa->toChars()); } void *TemplateAliasParameter::dummyArg() { RootObject *s = specAlias; if (!s) { if (!sdummy) sdummy = new Dsymbol(); s = sdummy; } return (void*)s; } RootObject *TemplateAliasParameter::specialization() { return specAlias; } RootObject *TemplateAliasParameter::defaultArg(Loc, Scope *sc) { RootObject *da = defaultAlias; Type *ta = isType(defaultAlias); if (ta) { if (ta->ty == Tinstance) { // If the default arg is a template, instantiate for each type da = ta->syntaxCopy(); } } RootObject *o = aliasParameterSemantic(loc, sc, da, NULL); // use the parameter loc return o; } bool TemplateAliasParameter::hasDefaultArg() { return defaultAlias != NULL; } /* ======================== TemplateValueParameter ========================== */ // value-parameter AA *TemplateValueParameter::edummies = 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() { return new TemplateValueParameter(loc, ident, valType->syntaxCopy(), specValue ? specValue->syntaxCopy() : NULL, defaultValue ? defaultValue->syntaxCopy() : NULL); } bool TemplateValueParameter::declareParameter(Scope *sc) { VarDeclaration *v = new VarDeclaration(loc, valType, ident, NULL); v->storage_class = STCtemplateparameter; return sc->insert(v) != NULL; } bool TemplateValueParameter::semantic(Scope *sc, TemplateParameters *) { valType = valType->semantic(loc, sc); return !isError(valType); } MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *, Objects *dedtypes, Declaration **psparam) { //printf("TemplateValueParameter::matchArg('%s')\n", ident->toChars()); MATCH m = MATCHexact; Expression *ei = isExpression(oarg); Type *vt; if (!ei && oarg) { Dsymbol *si = isDsymbol(oarg); FuncDeclaration *f = si ? si->isFuncDeclaration() : NULL; if (!f || !f->fbody || f->needThis()) goto Lnomatch; ei = new VarExp(loc, f); ei = ::semantic(ei, sc); /* If a function is really property-like, and then * it's CTFEable, ei will be a literal expression. */ unsigned int olderrors = global.startGagging(); ei = resolveProperties(sc, ei); ei = ei->ctfeInterpret(); if (global.endGagging(olderrors) || ei->op == TOKerror) goto Lnomatch; /* Bugzilla 14520: A property-like function can match to both * TemplateAlias and ValueParameter. But for template overloads, * it should always prefer alias parameter to be consistent * template match result. * * template X(alias f) { enum X = 1; } * template X(int val) { enum X = 2; } * int f1() { return 0; } // CTFEable * int f2(); // body-less function is not CTFEable * enum x1 = X!f1; // should be 1 * enum x2 = X!f2; // should be 1 * * e.g. The x1 value must be same even if the f1 definition will be moved * into di while stripping body code. */ m = MATCHconvert; } if (ei && ei->op == TOKvar) { // Resolve const variables that we had skipped earlier ei = ei->ctfeInterpret(); } //printf("\tvalType: %s, ty = %d\n", valType->toChars(), valType->ty); vt = valType->semantic(loc, sc); //printf("ei: %s, ei->type: %s\n", ei->toChars(), ei->type->toChars()); //printf("vt = %s\n", vt->toChars()); if (ei->type) { MATCH m2 = ei->implicitConvTo(vt); //printf("m: %d\n", m); if (m2 < m) m = m2; if (m <= MATCHnomatch) goto Lnomatch; ei = ei->implicitCastTo(sc, vt); ei = ei->ctfeInterpret(); } if (specValue) { if (!ei || (Expression *)dmd_aaGetRvalue(edummies, (void *)ei->type) == ei) goto Lnomatch; Expression *e = specValue; sc = sc->startCTFE(); e = ::semantic(e, sc); e = resolveProperties(sc, e); sc = sc->endCTFE(); e = e->implicitCastTo(sc, vt); e = e->ctfeInterpret(); ei = ei->syntaxCopy(); sc = sc->startCTFE(); ei = ::semantic(ei, sc); sc = sc->endCTFE(); ei = ei->implicitCastTo(sc, vt); ei = ei->ctfeInterpret(); //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)[i]) { // Must match already deduced value Expression *e = (Expression *)(*dedtypes)[i]; if (!ei || !ei->equals(e)) goto Lnomatch; } } (*dedtypes)[i] = ei; if (psparam) { Initializer *init = new ExpInitializer(loc, ei); Declaration *sparam = new VarDeclaration(loc, vt, ident, init); sparam->storage_class = STCmanifest; *psparam = sparam; } return dependent ? MATCHexact : m; Lnomatch: //printf("\tno match\n"); if (psparam) *psparam = NULL; return MATCHnomatch; } void TemplateValueParameter::print(RootObject *, RootObject *oded) { printf(" %s\n", ident->toChars()); Expression *ea = isExpression(oded); if (specValue) printf("\tSpecialization: %s\n", specValue->toChars()); printf("\tParameter Value: %s\n", ea ? ea->toChars() : "NULL"); } void *TemplateValueParameter::dummyArg() { Expression *e = specValue; if (!e) { // Create a dummy value Expression **pe = (Expression **)dmd_aaGet(&edummies, (void *)valType); if (!*pe) *pe = valType->defaultInit(); e = *pe; } return (void *)e; } RootObject *TemplateValueParameter::specialization() { return specValue; } RootObject *TemplateValueParameter::defaultArg(Loc instLoc, Scope *sc) { Expression *e = defaultValue; if (e) { e = e->syntaxCopy(); if ((e = ::semantic(e, sc)) == NULL) return NULL; if ((e = resolveProperties(sc, e)) == NULL) return NULL; e = e->resolveLoc(instLoc, sc); // use the instantiated loc e = e->optimize(WANTvalue); } return e; } bool TemplateValueParameter::hasDefaultArg() { return defaultValue != NULL; } /* ======================== TemplateTupleParameter ========================== */ // variadic-parameter TemplateTupleParameter::TemplateTupleParameter(Loc loc, Identifier *ident) : TemplateParameter(loc, ident) { this->ident = ident; } TemplateTupleParameter *TemplateTupleParameter::isTemplateTupleParameter() { return this; } TemplateParameter *TemplateTupleParameter::syntaxCopy() { return new TemplateTupleParameter(loc, ident); } bool TemplateTupleParameter::declareParameter(Scope *sc) { TypeIdentifier *ti = new TypeIdentifier(loc, ident); Declaration *ad = new AliasDeclaration(loc, ident, ti); return sc->insert(ad) != NULL; } bool TemplateTupleParameter::semantic(Scope *, TemplateParameters *) { return true; } MATCH TemplateTupleParameter::matchArg(Loc, Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam) { /* 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 (Tuple *u = isTuple((*dedtypes)[i])) { // It has already been deduced ovar = u; } else if (i + 1 == tiargs->dim && isTuple((*tiargs)[i])) ovar = isTuple((*tiargs)[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[j] = (*tiargs)[i + j]; } } return matchArg(sc, ovar, i, parameters, dedtypes, psparam); } MATCH TemplateTupleParameter::matchArg(Scope *, RootObject *oarg, size_t i, TemplateParameters *, Objects *dedtypes, Declaration **psparam) { //printf("TemplateTupleParameter::matchArg('%s')\n", ident->toChars()); Tuple *ovar = isTuple(oarg); if (!ovar) return MATCHnomatch; if ((*dedtypes)[i]) { Tuple *tup = isTuple((*dedtypes)[i]); if (!tup) return MATCHnomatch; if (!match(tup, ovar)) return MATCHnomatch; } (*dedtypes)[i] = ovar; if (psparam) *psparam = new TupleDeclaration(loc, ident, &ovar->objects); return dependent ? MATCHexact : MATCHconvert; } void TemplateTupleParameter::print(RootObject *, RootObject *oded) { printf(" %s... [", ident->toChars()); Tuple *v = isTuple(oded); assert(v); //printf("|%d| ", v->objects.dim); for (size_t i = 0; i < v->objects.dim; i++) { if (i) printf(", "); RootObject *o = v->objects[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::dummyArg() { return NULL; } RootObject *TemplateTupleParameter::specialization() { return NULL; } RootObject *TemplateTupleParameter::defaultArg(Loc, Scope *) { return NULL; } bool TemplateTupleParameter::hasDefaultArg() { return false; } /* ======================== TemplateInstance ================================ */ TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) : ScopeDsymbol(NULL) { this->loc = loc; this->name = ident; this->tiargs = NULL; this->tempdecl = NULL; this->inst = NULL; this->tinst = NULL; this->tnext = NULL; this->minst = NULL; this->deferred = NULL; this->memberOf = NULL; this->argsym = NULL; this->aliasdecl = NULL; this->semantictiargsdone = false; this->inuse = 0; this->nest = 0; this->havetempdecl = false; this->enclosing = NULL; this->gagged = false; this->hash = 0; this->fargs = 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) { this->loc = loc; this->name = td->ident; this->tiargs = tiargs; this->tempdecl = td; this->inst = NULL; this->tinst = NULL; this->tnext = NULL; this->minst = NULL; this->deferred = NULL; this->memberOf = NULL; this->argsym = NULL; this->aliasdecl = NULL; this->semantictiargsdone = true; this->inuse = 0; this->nest = 0; this->havetempdecl = true; this->enclosing = NULL; this->gagged = false; this->hash = 0; this->fargs = NULL; assert(tempdecl->_scope); } 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)[i] = objectSyntaxCopy((*objs)[i]); } return a; } Dsymbol *TemplateInstance::syntaxCopy(Dsymbol *s) { TemplateInstance *ti = s ? (TemplateInstance *)s : new TemplateInstance(loc, name); ti->tiargs = arraySyntaxCopy(tiargs); TemplateDeclaration *td; if (inst && tempdecl && (td = tempdecl->isTemplateDeclaration()) != NULL) td->ScopeDsymbol::syntaxCopy(ti); else ScopeDsymbol::syntaxCopy(ti); return ti; } void TemplateInstance::semantic(Scope *sc) { semantic(sc, NULL); } void TemplateInstance::expandMembers(Scope *sc2) { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->setScope(sc2); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->importAll(sc2); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; //printf("\t[%d] semantic on '%s' %p kind %s in '%s'\n", i, s->toChars(), s, s->kind(), this->toChars()); //printf("test: enclosing = %d, sc2->parent = %s\n", enclosing, sc2->parent->toChars()); // if (enclosing) // s->parent = sc->parent; //printf("test3: enclosing = %d, s->parent = %s\n", enclosing, s->parent->toChars()); s->semantic(sc2); //printf("test4: enclosing = %d, s->parent = %s\n", enclosing, s->parent->toChars()); Module::runDeferredSemantic(); } } void TemplateInstance::tryExpandMembers(Scope *sc2) { static int nest; // extracted to a function to allow windows SEH to work without destructors in the same function //printf("%d\n", nest); if (++nest > global.recursionLimit) { global.gag = 0; // ensure error message gets printed error("recursive expansion exceeded allowed nesting limit"); fatal(); } expandMembers(sc2); nest--; } void TemplateInstance::trySemantic3(Scope *sc2) { // extracted to a function to allow windows SEH to work without destructors in the same function static int nest; //printf("%d\n", nest); if (++nest > global.recursionLimit) { global.gag = 0; // ensure error message gets printed error("recursive expansion exceeded allowed nesting limit"); fatal(); } semantic3(sc2); --nest; } void TemplateInstance::semantic(Scope *sc, Expressions *fargs) { //printf("[%s] TemplateInstance::semantic('%s', this=%p, gag = %d, sc = %p)\n", loc.toChars(), toChars(), this, global.gag, sc); if (inst) // if semantic() was already run { return; } if (semanticRun != PASSinit) { Ungag ungag(global.gag); if (!gagged) global.gag = 0; error(loc, "recursive template expansion"); if (gagged) semanticRun = PASSinit; else inst = this; errors = true; return; } // Get the enclosing template instance from the scope tinst tinst = sc->tinst; // Get the instantiating module from the scope minst minst = sc->minst; // Bugzilla 10920: If the enclosing function is non-root symbol, // this instance should be speculative. if (!tinst && sc->func && sc->func->inNonRoot()) { minst = NULL; } gagged = (global.gag > 0); semanticRun = PASSsemantic; /* Find template declaration first, * then run semantic on each argument (place results in tiargs[]), * last find most specialized template from overload list/set. */ if (!findTempDecl(sc, NULL) || !semanticTiargs(sc) || !findBestMatch(sc, fargs)) { Lerror: if (gagged) { // Bugzilla 13220: Rollback status for later semantic re-running. semanticRun = PASSinit; } else inst = this; errors = true; return; } TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); // If tempdecl is a mixin, disallow it if (tempdecl->ismixin) { error("mixin templates are not regular templates"); goto Lerror; } hasNestedArgs(tiargs, tempdecl->isstatic); if (errors) goto Lerror; /* See if there is an existing TemplateInstantiation that already * implements the typeargs. If so, just refer to that one instead. */ inst = tempdecl->findExistingInstance(this, fargs); TemplateInstance *errinst = NULL; if (!inst) { // So, we need to implement 'this' instance. } else if (inst->gagged && !gagged && inst->errors) { // If the first instantiation had failed, re-run semantic, // so that error messages are shown. errinst = inst; } else { // It's a match parent = inst->parent; errors = inst->errors; // If both this and the previous instantiation were gagged, // use the number of errors that happened last time. global.errors += errors; global.gaggedErrors += errors; // If the first instantiation was gagged, but this is not: if (inst->gagged) { // It had succeeded, mark it is a non-gagged instantiation, // and reuse it. inst->gagged = gagged; } this->tnext = inst->tnext; inst->tnext = this; /* A module can have explicit template instance and its alias * in module scope (e,g, `alias Base64 = Base64Impl!('+', '/');`). * If the first instantiation 'inst' had happened in non-root module, * compiler can assume that its instantiated code would be included * in the separately compiled obj/lib file (e.g. phobos.lib). * * However, if 'this' second instantiation happened in root module, * compiler might need to invoke its codegen (Bugzilla 2500 & 2644). * But whole import graph is not determined until all semantic pass finished, * so 'inst' should conservatively finish the semantic3 pass for the codegen. */ if (minst && minst->isRoot() && !(inst->minst && inst->minst->isRoot())) { /* Swap the position of 'inst' and 'this' in the instantiation graph. * Then, the primary instance `inst` will be changed to a root instance, * along with all members of `inst` having their scopes updated. * * Before: * non-root -> A!() -> B!()[inst] -> C!() { members[non-root] } * | * root -> D!() -> B!()[this] * * After: * non-root -> A!() -> B!()[this] * | * root -> D!() -> B!()[inst] -> C!() { members[root] } */ Module *mi = minst; TemplateInstance *ti = tinst; minst = inst->minst; tinst = inst->tinst; inst->minst = mi; inst->tinst = ti; /* https://issues.dlang.org/show_bug.cgi?id=21299 `minst` has been updated on the primary instance `inst` so it is now coming from a root module, however all Dsymbol `inst.members` of the instance still have their `_scope.minst` pointing at the original non-root module. We must now propagate `minst` to all members so that forward referenced dependencies that get instantiated will also be appended to the root module, otherwise there will be undefined references at link-time. */ class InstMemberWalker : public Visitor { public: TemplateInstance *inst; InstMemberWalker(TemplateInstance *inst) : inst(inst) { } void visit(Dsymbol *d) { if (d->_scope) d->_scope->minst = inst->minst; } void visit(ScopeDsymbol *sds) { if (!sds->members) return; for (size_t i = 0; i < sds->members->dim; i++) { Dsymbol *s = (*sds->members)[i]; s->accept(this); } visit((Dsymbol *)sds); } void visit(AttribDeclaration *ad) { Dsymbols *d = ad->include(NULL, NULL); if (!d) return; for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = (*d)[i]; s->accept(this); } visit((Dsymbol *)ad); } void visit(ConditionalDeclaration *cd) { if (cd->condition->inc) visit((AttribDeclaration *)cd); else visit((Dsymbol *)cd); } }; InstMemberWalker v(inst); inst->accept(&v); if (minst) // if inst was not speculative { /* Add 'inst' once again to the root module members[], then the * instance members will get codegen chances. */ inst->appendToModuleMember(); } } return; } unsigned errorsave = global.errors; inst = this; parent = enclosing ? enclosing : tempdecl->parent; //printf("parent = '%s'\n", parent->kind()); TemplateInstance *tempdecl_instance_idx = tempdecl->addInstance(this); //getIdent(); // Store the place we added it to in target_symbol_list(_idx) so we can // remove it later if we encounter an error. Dsymbols *target_symbol_list = appendToModuleMember(); size_t target_symbol_list_idx = target_symbol_list ? target_symbol_list->dim - 1 : 0; // Copy the syntax trees from the TemplateDeclaration members = Dsymbol::arraySyntaxCopy(tempdecl->members); // resolve TemplateThisParameter for (size_t i = 0; i < tempdecl->parameters->dim; i++) { if ((*tempdecl->parameters)[i]->isTemplateThisParameter() == NULL) continue; Type *t = isType((*tiargs)[i]); assert(t); if (StorageClass stc = ModToStc(t->mod)) { //printf("t = %s, stc = x%llx\n", t->toChars(), stc); Dsymbols *s = new Dsymbols(); s->push(new StorageClassDeclaration(stc, members)); members = s; } break; } // Create our own scope for the template parameters Scope *scope = tempdecl->_scope; if (tempdecl->semanticRun == PASSinit) { error("template instantiation %s forward references template declaration %s", toChars(), tempdecl->toChars()); return; } argsym = new ScopeDsymbol(); argsym->parent = scope->parent; scope = scope->push(argsym); scope->tinst = this; scope->minst = minst; //scope->stc = 0; // Declare each template parameter as an alias for the argument type Scope *paramscope = scope->push(); paramscope->stc = 0; paramscope->protection = Prot(PROTpublic); // Bugzilla 14169: template parameters should be public declareParameters(paramscope); paramscope->pop(); // Add members of template instance to template instance symbol table // parent = scope->scopesym; symtab = new DsymbolTable(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->addMember(scope, this); } /* 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, tempdecl->ident) && s) { //printf("tempdecl->ident = %s, s = '%s'\n", tempdecl->ident->toChars(), s->kind(), s->toPrettyChars()); //printf("setting aliasdecl\n"); aliasdecl = s; } } /* If function template declaration */ if (fargs && aliasdecl) { FuncDeclaration *fd = aliasdecl->isFuncDeclaration(); if (fd) { /* Transmit fargs to type so that TypeFunction::semantic() can * resolve any "auto ref" storage classes. */ TypeFunction *tf = (TypeFunction *)fd->type; if (tf && tf->ty == Tfunction) tf->fargs = fargs; } } // Do semantic() analysis on template instance members Scope *sc2; sc2 = scope->push(this); //printf("enclosing = %d, sc->parent = %s\n", enclosing, sc->parent->toChars()); sc2->parent = this; sc2->tinst = this; sc2->minst = minst; tryExpandMembers(sc2); semanticRun = PASSsemanticdone; /* ConditionalDeclaration may introduce eponymous declaration, * so we should find it once again after semantic. */ if (members->dim) { Dsymbol *s; if (Dsymbol::oneMembers(members, &s, tempdecl->ident) && s) { if (!aliasdecl || aliasdecl != s) { //printf("tempdecl->ident = %s, s = '%s'\n", tempdecl->ident->toChars(), s->kind(), s->toPrettyChars()); //printf("setting aliasdecl 2\n"); aliasdecl = s; } } } if (global.errors != errorsave) goto Laftersemantic; /* If any of the instantiation members didn't get semantic() run * on them due to forward references, we cannot run semantic2() * or semantic3() yet. */ { bool found_deferred_ad = false; for (size_t i = 0; i < Module::deferred.dim; i++) { Dsymbol *sd = Module::deferred[i]; AggregateDeclaration *ad = sd->isAggregateDeclaration(); if (ad && ad->parent && ad->parent->isTemplateInstance()) { //printf("deferred template aggregate: %s %s\n", // sd->parent->toChars(), sd->toChars()); found_deferred_ad = true; if (ad->parent == this) { ad->deferred = this; break; } } } if (found_deferred_ad || Module::deferred.dim) 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 (global.errors != errorsave) goto Laftersemantic; if ((sc->func || (sc->flags & SCOPEfullinst)) && !tinst) { /* If a template is instantiated inside function, the whole instantiation * should be done at that position. But, immediate running semantic3 of * dependent templates may cause unresolved forward reference (Bugzilla 9050). * To avoid the issue, don't run semantic3 until semantic and semantic2 done. */ TemplateInstances deferred; this->deferred = &deferred; //printf("Run semantic3 on %s\n", toChars()); trySemantic3(sc2); for (size_t i = 0; i < deferred.dim; i++) { //printf("+ run deferred semantic3 on %s\n", deferred[i]->toChars()); deferred[i]->semantic3(NULL); } this->deferred = NULL; } else if (tinst) { bool doSemantic3 = false; if (sc->func && aliasdecl && aliasdecl->toAlias()->isFuncDeclaration()) { /* Template function instantiation should run semantic3 immediately * for attribute inference. */ trySemantic3(sc2); } else if (sc->func) { /* A lambda function in template arguments might capture the * instantiated scope context. For the correct context inference, * all instantiated functions should run the semantic3 immediately. * See also compilable/test14973.d */ for (size_t i = 0; i < tdtypes.dim; i++) { RootObject *oarg = tdtypes[i]; Dsymbol *s = getDsymbol(oarg); if (!s) continue; if (TemplateDeclaration *td = s->isTemplateDeclaration()) { if (!td->literal) continue; assert(td->members && td->members->dim == 1); s = (*td->members)[0]; } if (FuncLiteralDeclaration *fld = s->isFuncLiteralDeclaration()) { if (fld->tok == TOKreserved) { doSemantic3 = true; break; } } } //printf("[%s] %s doSemantic3 = %d\n", loc.toChars(), toChars(), doSemantic3); } if (doSemantic3) trySemantic3(sc2); TemplateInstance *ti = tinst; int nest = 0; while (ti && !ti->deferred && ti->tinst) { ti = ti->tinst; if (++nest > global.recursionLimit) { global.gag = 0; // ensure error message gets printed error("recursive expansion"); fatal(); } } if (ti && ti->deferred) { //printf("deferred semantic3 of %p %s, ti = %s, ti->deferred = %p\n", this, toChars(), ti->toChars()); for (size_t i = 0; ; i++) { if (i == ti->deferred->dim) { ti->deferred->push(this); break; } if ((*ti->deferred)[i] == this) break; } } } if (aliasdecl) { /* Bugzilla 13816: AliasDeclaration tries to resolve forward reference * twice (See inuse check in AliasDeclaration::toAlias()). It's * necessary to resolve mutual references of instantiated symbols, but * it will left a true recursive alias in tuple declaration - an * AliasDeclaration A refers TupleDeclaration B, and B contains A * in its elements. To correctly make it an error, we strictly need to * resolve the alias of eponymous member. */ aliasdecl = aliasdecl->toAlias2(); } Laftersemantic: sc2->pop(); scope->pop(); // Give additional context info if error occurred during instantiation if (global.errors != errorsave) { if (!errors) { if (!tempdecl->literal) error(loc, "error instantiating"); if (tinst) tinst->printInstantiationTrace(); } errors = true; if (gagged) { // Errors are gagged, so remove the template instance from the // instance/symbol lists we added it to and reset our state to // finish clean and so we can try to instantiate it again later // (see bugzilla 4302 and 6602). tempdecl->removeInstance(tempdecl_instance_idx); if (target_symbol_list) { // Because we added 'this' in the last position above, we // should be able to remove it without messing other indices up. assert((*target_symbol_list)[target_symbol_list_idx] == this); target_symbol_list->remove(target_symbol_list_idx); memberOf = NULL; // no longer a member } semanticRun = PASSinit; inst = NULL; symtab = NULL; } } else if (errinst) { /* Bugzilla 14541: If the previous gagged instance had failed by * circular references, currrent "error reproduction instantiation" * might succeed, because of the difference of instantiated context. * On such case, the cached error instance needs to be overridden by the * succeeded instance. */ //printf("replaceInstance()\n"); TemplateInstances *tinstances = (TemplateInstances *)dmd_aaGetRvalue((AA *)tempdecl->instances, (void *)hash); assert(tinstances); for (size_t i = 0; i < tinstances->dim; i++) { TemplateInstance *ti = (*tinstances)[i]; if (ti == errinst) { (*tinstances)[i] = this; // override break; } } } } /********************************************** * Find template declaration corresponding to template instance. * * Returns: * false if finding fails. * Note: * This function is reentrant against error occurrence. If returns false, * any members of this object won't be modified, and repetition call will * reproduce same error. */ bool TemplateInstance::findTempDecl(Scope *sc, WithScopeSymbol **pwithsym) { if (pwithsym) *pwithsym = NULL; if (havetempdecl) return true; //printf("TemplateInstance::findTempDecl() %s\n", toChars()); if (!tempdecl) { /* Given: * foo!( ... ) * figure out which TemplateDeclaration foo refers to. */ Identifier *id = name; Dsymbol *scopesym; Dsymbol *s = sc->search(loc, id, &scopesym); if (!s) { s = sc->search_correct(id); if (s) error("template '%s' is not defined, did you mean %s?", id->toChars(), s->toChars()); else error("template '%s' is not defined", id->toChars()); return false; } if (pwithsym) *pwithsym = 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->tempdecl && ti->tempdecl->ident == id) { /* 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) */ TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration(); assert(td); if (td->overroot) // if not start of overloaded list of TemplateDeclaration's td = td->overroot; // then get the start s = td; } } if (!updateTempDecl(sc, s)) { return false; } } assert(tempdecl); struct ParamFwdTi { static int fp(void *param, Dsymbol *s) { TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) return 0; TemplateInstance *ti = (TemplateInstance *)param; if (td->semanticRun == PASSinit) { if (td->_scope) { // Try to fix forward reference. Ungag errors while doing so. Ungag ungag = td->ungagSpeculative(); td->semantic(td->_scope); } if (td->semanticRun == PASSinit) { ti->error("%s forward references template declaration %s", ti->toChars(), td->toChars()); return 1; } } return 0; } }; // Look for forward references OverloadSet *tovers = tempdecl->isOverloadSet(); size_t overs_dim = tovers ? tovers->a.dim : 1; for (size_t oi = 0; oi < overs_dim; oi++) { if (overloadApply(tovers ? tovers->a[oi] : tempdecl, (void *)this, &ParamFwdTi::fp)) return false; } return true; } /********************************************** * Confirm s is a valid template, then store it. * Input: * sc * s candidate symbol of template. It may be: * TemplateDeclaration * FuncDeclaration with findTemplateDeclRoot() != NULL * OverloadSet which contains candidates * Returns: * true if updating succeeds. */ bool TemplateInstance::updateTempDecl(Scope *sc, Dsymbol *s) { if (s) { Identifier *id = name; s = s->toAlias(); /* If an OverloadSet, look for a unique member that is a template declaration */ OverloadSet *os = s->isOverloadSet(); if (os) { s = NULL; for (size_t i = 0; i < os->a.dim; i++) { Dsymbol *s2 = os->a[i]; if (FuncDeclaration *f = s2->isFuncDeclaration()) s2 = f->findTemplateDeclRoot(); else s2 = s2->isTemplateDeclaration(); if (s2) { if (s) { tempdecl = os; return true; } s = s2; } } if (!s) { error("template '%s' is not defined", id->toChars()); return false; } } OverDeclaration *od = s->isOverDeclaration(); if (od) { tempdecl = od; // TODO: more strict check return true; } /* It should be a TemplateDeclaration, not some other symbol */ if (FuncDeclaration *f = s->isFuncDeclaration()) tempdecl = f->findTemplateDeclRoot(); else tempdecl = s->isTemplateDeclaration(); if (!tempdecl) { if (!s->parent && global.errors) return false; 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 false; } s = s2; } //assert(s->parent); TemplateInstance *ti = s->parent ? s->parent->isTemplateInstance() : NULL; if (ti && (ti->name == s->ident || ti->toAlias()->ident == s->ident) && 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) */ TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration(); assert(td); if (td->overroot) // if not start of overloaded list of TemplateDeclaration's td = td->overroot; // then get the start tempdecl = td; } else { error("%s is not a template declaration, it is a %s", id->toChars(), s->kind()); return false; } } } return (tempdecl != NULL); } /********************************** * Run semantic on the elements of tiargs. * Input: * sc * Returns: * false if one or more arguments have errors. * Note: * This function is reentrant against error occurrence. If returns false, * all elements of tiargs won't be modified. */ bool TemplateInstance::semanticTiargs(Scope *sc) { //printf("+TemplateInstance::semanticTiargs() %s\n", toChars()); if (semantictiargsdone) return true; if (semanticTiargs(loc, sc, tiargs, 0)) { // cache the result iff semantic analysis succeeded entirely semantictiargsdone = 1; return true; } return false; } /********************************** * Run semantic of tiargs as arguments of template. * Input: * loc * sc * tiargs array of template arguments * flags 1: replace const variables with their initializers * 2: don't devolve Parameter to Type * Returns: * false if one or more arguments have errors. */ bool 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 true; bool err = false; for (size_t j = 0; j < tiargs->dim; j++) { RootObject *o = (*tiargs)[j]; Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); //printf("1: (*tiargs)[%d] = %p, s=%p, v=%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) goto Lexpr; if (sa) goto Ldsym; if (ta == NULL) { assert(global.errors); ta = Type::terror; } Ltype: 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++) { Parameter *arg = (*tt->arguments)[i]; if (flags & 2 && arg->ident) tiargs->insert(j + i, arg); else tiargs->insert(j + i, arg->type); } } j--; continue; } if (ta->ty == Terror) { err = true; continue; } (*tiargs)[j] = ta->merge2(); } else if (ea) { Lexpr: //printf("+[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars()); if (flags & 1) // only used by __traits { ea = ::semantic(ea, sc); // must not interpret the args, excepting template parameters if (ea->op != TOKvar || (((VarExp *)ea)->var->storage_class & STCtemplateparameter)) { ea = ea->optimize(WANTvalue); } } else { sc = sc->startCTFE(); ea = ::semantic(ea, sc); sc = sc->endCTFE(); if (ea->op == TOKvar) { /* 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(). */ } else if (definitelyValueParameter(ea)) { if (ea->checkValue()) // check void expression ea = new ErrorExp(); unsigned int olderrs = global.errors; ea = ea->ctfeInterpret(); if (global.errors != olderrs) ea = new ErrorExp(); } } //printf("-[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars()); if (ea->op == TOKtuple) { // Expand tuple TupleExp *te = (TupleExp *)ea; size_t dim = te->exps->dim; tiargs->remove(j); if (dim) { tiargs->reserve(dim); for (size_t i = 0; i < dim; i++) tiargs->insert(j + i, (*te->exps)[i]); } j--; continue; } if (ea->op == TOKerror) { err = true; continue; } (*tiargs)[j] = ea; if (ea->op == TOKtype) { ta = ea->type; goto Ltype; } if (ea->op == TOKscope) { sa = ((ScopeExp *)ea)->sds; goto Ldsym; } if (ea->op == TOKfunction) { FuncExp *fe = (FuncExp *)ea; /* A function literal, that is passed to template and * already semanticed as function pointer, never requires * outer frame. So convert it to global function is valid. */ if (fe->fd->tok == TOKreserved && fe->type->ty == Tpointer) { // change to non-nested fe->fd->tok = TOKfunction; fe->fd->vthis = NULL; } else if (fe->td) { /* If template argument is a template lambda, * get template declaration itself. */ //sa = fe->td; //goto Ldsym; } } if (ea->op == TOKdotvar) { // translate expression to dsymbol. sa = ((DotVarExp *)ea)->var; goto Ldsym; } if (ea->op == TOKtemplate) { sa = ((TemplateExp *)ea)->td; goto Ldsym; } if (ea->op == TOKdottd) { // translate expression to dsymbol. sa = ((DotTemplateExp *)ea)->td; goto Ldsym; } } else if (sa) { Ldsym: //printf("dsym %s %s\n", sa->kind(), sa->toChars()); if (sa->errors) { err = true; continue; } TupleDeclaration *d = sa->toAlias()->isTupleDeclaration(); if (d) { // Expand tuple tiargs->remove(j); tiargs->insert(j, d->objects); j--; continue; } if (FuncAliasDeclaration *fa = sa->isFuncAliasDeclaration()) { FuncDeclaration *f = fa->toAliasFunc(); if (!fa->hasOverloads && f->isUnique()) { // Strip FuncAlias only when the aliased function // does not have any overloads. sa = f; } } (*tiargs)[j] = sa; TemplateDeclaration *td = sa->isTemplateDeclaration(); if (td && td->semanticRun == PASSinit && td->literal) { td->semantic(sc); } FuncDeclaration *fd = sa->isFuncDeclaration(); if (fd) fd->functionSemantic(); } else if (isParameter(o)) { } else { assert(0); } //printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]); } return !err; } bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) { if (havetempdecl) { TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); assert(tempdecl->_scope); // Deduce tdtypes tdtypes.setDim(tempdecl->parameters->dim); if (!tempdecl->matchWithInstance(sc, this, &tdtypes, fargs, 2)) { error("incompatible arguments for template instantiation"); return false; } // TODO: Normalizing tiargs for bugzilla 7469 is necessary? return true; } unsigned errs = global.errors; struct ParamBest { // context Scope *sc; TemplateInstance *ti; Objects dedtypes; // result TemplateDeclaration *td_best; TemplateDeclaration *td_ambig; MATCH m_best; static int fp(void *param, Dsymbol *s) { return ((ParamBest *)param)->fp(s); } int fp(Dsymbol *s) { TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) return 0; if (td == td_best) // skip duplicates return 0; //printf("td = %s\n", td->toPrettyChars()); // If more arguments than parameters, // then this is no match. if (td->parameters->dim < ti->tiargs->dim) { if (!td->isVariadic()) return 0; } dedtypes.setDim(td->parameters->dim); dedtypes.zero(); assert(td->semanticRun != PASSinit); MATCH m = td->matchWithInstance(sc, ti, &dedtypes, ti->fargs, 0); //printf("matchWithInstance = %d\n", m); if (m <= MATCHnomatch) // no match at all return 0; if (m < m_best) goto Ltd_best; if (m > m_best) goto Ltd; { // Disambiguate by picking the most specialized TemplateDeclaration MATCH c1 = td->leastAsSpecialized(sc, td_best, ti->fargs); MATCH c2 = td_best->leastAsSpecialized(sc, td, ti->fargs); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; } td_ambig = td; return 0; Ltd_best: // td_best is the best match so far td_ambig = NULL; return 0; Ltd: // td is the new best match td_ambig = NULL; td_best = td; m_best = m; ti->tdtypes.setDim(dedtypes.dim); memcpy(ti->tdtypes.tdata(), dedtypes.tdata(), ti->tdtypes.dim * sizeof(void *)); return 0; } }; ParamBest p; // context p.ti = this; p.sc = sc; /* Since there can be multiple TemplateDeclaration's with the same * name, look for the best match. */ TemplateDeclaration *td_last = NULL; OverloadSet *tovers = tempdecl->isOverloadSet(); size_t overs_dim = tovers ? tovers->a.dim : 1; for (size_t oi = 0; oi < overs_dim; oi++) { // result p.td_best = NULL; p.td_ambig = NULL; p.m_best = MATCHnomatch; overloadApply(tovers ? tovers->a[oi] : tempdecl, &p, &ParamBest::fp); if (p.td_ambig) { ::error(loc, "%s %s.%s matches more than one template declaration:\n%s: %s\nand\n%s: %s", p.td_best->kind(), p.td_best->parent->toPrettyChars(), p.td_best->ident->toChars(), p.td_best->loc.toChars() , p.td_best->toChars(), p.td_ambig->loc.toChars(), p.td_ambig->toChars()); return false; } if (p.td_best) { if (!td_last) td_last = p.td_best; else if (td_last != p.td_best) { ScopeDsymbol::multiplyDefined(loc, td_last, p.td_best); return false; } } } if (td_last) { /* Bugzilla 7469: Normalize tiargs by using corresponding deduced * template value parameters and tuples for the correct mangling. * * By doing this before hasNestedArgs, CTFEable local variable will be * accepted as a value parameter. For example: * * void foo() { * struct S(int n) {} // non-global template * const int num = 1; // CTFEable local variable * S!num s; // S!1 is instantiated, not S!num * } */ size_t dim = td_last->parameters->dim - (td_last->isVariadic() ? 1 : 0); for (size_t i = 0; i < dim; i++) { if (tiargs->dim <= i) tiargs->push(tdtypes[i]); assert(i < tiargs->dim); TemplateValueParameter *tvp = (*td_last->parameters)[i]->isTemplateValueParameter(); if (!tvp) continue; assert(tdtypes[i]); // tdtypes[i] is already normalized to the required type in matchArg (*tiargs)[i] = tdtypes[i]; } if (td_last->isVariadic() && tiargs->dim == dim && tdtypes[dim]) { Tuple *va = isTuple(tdtypes[dim]); assert(va); for (size_t i = 0; i < va->objects.dim; i++) tiargs->push(va->objects[i]); } } else if (errors && inst) { // instantiation was failed with error reporting assert(global.errors); return false; } else { TemplateDeclaration *tdecl = tempdecl->isTemplateDeclaration(); if (errs != global.errors) errorSupplemental(loc, "while looking for match for %s", toChars()); else if (tdecl && !tdecl->overnext) { // Only one template, so we can give better error message error("does not match template declaration %s", tdecl->toChars()); } else ::error(loc, "%s %s.%s does not match any template declaration", tempdecl->kind(), tempdecl->parent->toPrettyChars(), tempdecl->ident->toChars()); return false; } /* The best match is td_last */ tempdecl = td_last; return (errs == global.errors); } /***************************************************** * Determine if template instance is really a template function, * and that template function needs to infer types from the function * arguments. * * Like findBestMatch, iterate possible template candidates, * but just looks only the necessity of type inference. */ bool TemplateInstance::needsTypeInference(Scope *sc, int flag) { //printf("TemplateInstance::needsTypeInference() %s\n", toChars()); if (semanticRun != PASSinit) return false; struct ParamNeedsInf { // context Scope *sc; TemplateInstance *ti; int flag; // result Objects dedtypes; size_t count; static int fp(void *param, Dsymbol *s) { return ((ParamNeedsInf *)param)->fp(s); } int fp(Dsymbol *s) { TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) { return 0; } /* If any of the overloaded template declarations need inference, * then return true */ FuncDeclaration *fd; if (!td->onemember) return 0; if (TemplateDeclaration *td2 = td->onemember->isTemplateDeclaration()) { if (!td2->onemember || !td2->onemember->isFuncDeclaration()) return 0; if (ti->tiargs->dim >= td->parameters->dim - (td->isVariadic() ? 1 : 0)) return 0; return 1; } if ((fd = td->onemember->isFuncDeclaration()) == NULL || fd->type->ty != Tfunction) { return 0; } for (size_t i = 0; i < td->parameters->dim; i++) { if ((*td->parameters)[i]->isTemplateThisParameter()) return 1; } /* Determine if the instance arguments, tiargs, are all that is necessary * to instantiate the template. */ //printf("tp = %p, td->parameters->dim = %d, tiargs->dim = %d\n", tp, td->parameters->dim, ti->tiargs->dim); TypeFunction *tf = (TypeFunction *)fd->type; if (size_t dim = Parameter::dim(tf->parameters)) { TemplateParameter *tp = td->isVariadic(); if (tp && td->parameters->dim > 1) return 1; if (!tp && ti->tiargs->dim < td->parameters->dim) { // Can remain tiargs be filled by default arguments? for (size_t i = ti->tiargs->dim; i < td->parameters->dim; i++) { if (!(*td->parameters)[i]->hasDefaultArg()) return 1; } } for (size_t i = 0; i < dim; i++) { // 'auto ref' needs inference. if (Parameter::getNth(tf->parameters, i)->storageClass & STCauto) return 1; } } if (!flag) { /* Calculate the need for overload resolution. * When only one template can match with tiargs, inference is not necessary. */ dedtypes.setDim(td->parameters->dim); dedtypes.zero(); if (td->semanticRun == PASSinit) { if (td->_scope) { // Try to fix forward reference. Ungag errors while doing so. Ungag ungag = td->ungagSpeculative(); td->semantic(td->_scope); } if (td->semanticRun == PASSinit) { ti->error("%s forward references template declaration %s", ti->toChars(), td->toChars()); return 1; } } assert(td->semanticRun != PASSinit); MATCH m = td->matchWithInstance(sc, ti, &dedtypes, NULL, 0); if (m <= MATCHnomatch) return 0; } /* If there is more than one function template which matches, we may * need type inference (see Bugzilla 4430) */ if (++count > 1) return 1; return 0; } }; ParamNeedsInf p; // context p.ti = this; p.sc = sc; p.flag = flag; // result p.count = 0; OverloadSet *tovers = tempdecl->isOverloadSet(); size_t overs_dim = tovers ? tovers->a.dim : 1; unsigned olderrs = global.errors; for (size_t oi = 0; oi < overs_dim; oi++) { if (overloadApply(tovers ? tovers->a[oi] : tempdecl, &p, &ParamNeedsInf::fp)) return true; } if (olderrs != global.errors) { if (!global.gag) { errorSupplemental(loc, "while looking for match for %s", toChars()); semanticRun = PASSsemanticdone; inst = this; } errors = true; } //printf("false\n"); return false; } /***************************************** * Determines if a TemplateInstance will need a nested * generation of the TemplateDeclaration. * Sets enclosing property if so, and returns != 0; */ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) { int nested = 0; //printf("TemplateInstance::hasNestedArgs('%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++) { RootObject *o = (*args)[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 == TOKthis) { sa = ((ThisExp *)ea)->var; goto Lsa; } if (ea->op == TOKfunction) { if (((FuncExp *)ea)->td) sa = ((FuncExp *)ea)->td; else sa = ((FuncExp *)ea)->fd; goto Lsa; } // Emulate Expression::toMangleBuffer call that had exist in TemplateInstance::genIdent. if (ea->op != TOKint64 && ea->op != TOKfloat64 && ea->op != TOKcomplex80 && ea->op != TOKnull && ea->op != TOKstring && ea->op != TOKarrayliteral && ea->op != TOKassocarrayliteral && ea->op != TOKstructliteral) { ea->error("expression %s is not a valid template value argument", ea->toChars()); errors = true; } } else if (sa) { Lsa: sa = sa->toAlias(); TemplateDeclaration *td = sa->isTemplateDeclaration(); if (td) { TemplateInstance *ti = sa->toParent()->isTemplateInstance(); if (ti && ti->enclosing) sa = ti; } TemplateInstance *ti = sa->isTemplateInstance(); Declaration *d = sa->isDeclaration(); if ((td && td->literal) || (ti && ti->enclosing) || (d && !d->isDataseg() && !(d->storage_class & STCmanifest) && (!d->isFuncDeclaration() || d->isFuncDeclaration()->isNested()) && !isTemplateMixin() )) { // if module level template if (isstatic) { Dsymbol *dparent = sa->toParent2(); if (!enclosing) enclosing = dparent; else if (enclosing != dparent) { /* Select the more deeply nested of the two. * Error if one is not nested inside the other. */ for (Dsymbol *p = enclosing; p; p = p->parent) { if (p == dparent) goto L1; // enclosing is most nested } for (Dsymbol *p = dparent; p; p = p->parent) { if (p == enclosing) { enclosing = dparent; goto L1; // dparent is most nested } } error("%s is nested in both %s and %s", toChars(), enclosing->toChars(), dparent->toChars()); errors = true; } L1: //printf("\tnested inside %s\n", enclosing->toChars()); nested |= 1; } else { error("cannot use local '%s' as parameter to non-global template %s", sa->toChars(), tempdecl->toChars()); errors = true; } } } else if (va) { nested |= (int)hasNestedArgs(&va->objects, isstatic); } } //printf("-TemplateInstance::hasNestedArgs('%s') = %d\n", tempdecl->ident->toChars(), nested); return nested != 0; } /***************************************** * Append 'this' to the specific module members[] */ Dsymbols *TemplateInstance::appendToModuleMember() { Module *mi = minst; // instantiated -> inserted module if (global.params.useUnitTests || global.params.debuglevel) { // Turn all non-root instances to speculative if (mi && !mi->isRoot()) mi = NULL; } //printf("%s->appendToModuleMember() enclosing = %s mi = %s\n", // toPrettyChars(), // enclosing ? enclosing->toPrettyChars() : NULL, // mi ? mi->toPrettyChars() : NULL); if (!mi || mi->isRoot()) { /* If the instantiated module is speculative or root, insert to the * member of a root module. Then: * - semantic3 pass will get called on the instance members. * - codegen pass will get a selection chance to do/skip it. */ struct N { static Dsymbol *getStrictEnclosing(TemplateInstance *ti) { do { if (ti->enclosing) return ti->enclosing; ti = ti->tempdecl->isInstantiated(); } while (ti); return NULL; } }; Dsymbol *enc = N::getStrictEnclosing(this); // insert target is made stable by using the module // where tempdecl is declared. mi = (enc ? enc : tempdecl)->getModule(); if (!mi->isRoot()) mi = mi->importedFrom; assert(mi->isRoot()); } else { /* If the instantiated module is non-root, insert to the member of the * non-root module. Then: * - semantic3 pass won't be called on the instance. * - codegen pass won't reach to the instance. */ } //printf("\t--> mi = %s\n", mi->toPrettyChars()); if (memberOf == mi) // already a member { return NULL; } Dsymbols *a = mi->members; a->push(this); memberOf = mi; if (mi->semanticRun >= PASSsemantic2done && mi->isRoot()) Module::addDeferredSemantic2(this); if (mi->semanticRun >= PASSsemantic3done && mi->isRoot()) Module::addDeferredSemantic3(this); return a; } /**************************************** * 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(Objects *args) { TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); //printf("TemplateInstance::genIdent('%s')\n", tempdecl->ident->toChars()); OutBuffer buf; const char *id = tempdecl->ident->toChars(); if (!members) { // Use "__U" for the symbols declared inside template constraint. buf.printf("__U%llu%s", (ulonglong)strlen(id), id); } else buf.printf("__T%llu%s", (ulonglong)strlen(id), id); size_t nparams = tempdecl->parameters->dim - (tempdecl->isVariadic() ? 1 : 0); for (size_t i = 0; i < args->dim; i++) { RootObject *o = (*args)[i]; Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Tuple *va = isTuple(o); //printf("\to [%d] %p ta %p ea %p sa %p va %p\n", i, o, ta, ea, sa, va); if (i < nparams && (*tempdecl->parameters)[i]->specialization()) buf.writeByte('H'); // Bugzilla 6574 if (ta) { buf.writeByte('T'); if (ta->deco) buf.writestring(ta->deco); else { assert(global.errors); } } else if (ea) { // Don't interpret it yet, it might actually be an alias template parameter. // Only constfold manifest constants, not const/immutable lvalues, see https://issues.dlang.org/show_bug.cgi?id=17339. const bool keepLvalue = true; ea = ea->optimize(WANTvalue, keepLvalue); if (ea->op == TOKvar) { sa = ((VarExp *)ea)->var; ea = NULL; goto Lsa; } if (ea->op == TOKthis) { sa = ((ThisExp *)ea)->var; ea = NULL; goto Lsa; } if (ea->op == TOKfunction) { if (((FuncExp *)ea)->td) sa = ((FuncExp *)ea)->td; else 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; } // Now that we know it is not an alias, we MUST obtain a value unsigned olderr = global.errors; ea = ea->ctfeInterpret(); if (ea->op == TOKerror || olderr != global.errors) continue; /* Use deco that matches what it would be for a function parameter */ buf.writestring(ea->type->deco); mangleToBuffer(ea, &buf); } else if (sa) { Lsa: buf.writeByte('S'); sa = sa->toAlias(); Declaration *d = sa->isDeclaration(); if (d && (!d->type || !d->type->deco)) { error("forward reference of %s %s", d->kind(), d->toChars()); continue; } OutBuffer bufsa; mangleToBuffer(sa, &bufsa); const char *s = bufsa.extractString(); /* Bugzilla 3043: if the first character of s is a digit this * causes ambiguity issues because the digits of the two numbers are adjacent. * Current demanglers resolve this by trying various places to separate the * numbers until one gets a successful demangle. * Unfortunately, fixing this ambiguity will break existing binary * compatibility and the demanglers, so we'll leave it as is. */ buf.printf("%u%s", (unsigned)strlen(s), s); } else if (va) { assert(i + 1 == args->dim); // must be last one args = &va->objects; i = -(size_t)1; } else assert(0); } buf.writeByte('Z'); id = buf.peekString(); //printf("\tgenIdent = %s\n", id); return Identifier::idPool(id); } /************************************* * Lazily generate identifier for template instance. * This is because 75% of the ident's are never needed. */ Identifier *TemplateInstance::getIdent() { if (!ident && inst && !errors) ident = genIdent(tiargs); // need an identifier for name mangling purposes. return ident; } /**************************************************** * Declare parameters of template instance, initialize them with the * template instance arguments. */ void TemplateInstance::declareParameters(Scope *sc) { TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); //printf("TemplateInstance::declareParameters()\n"); for (size_t i = 0; i < tdtypes.dim; i++) { TemplateParameter *tp = (*tempdecl->parameters)[i]; //RootObject *o = (*tiargs)[i]; RootObject *o = tdtypes[i]; // initializer for tp //printf("\ttdtypes[%d] = %p\n", i, o); tempdecl->declareParameter(sc, tp, o); } } void TemplateInstance::semantic2(Scope *sc) { if (semanticRun >= PASSsemantic2) return; semanticRun = PASSsemantic2; if (!errors && members) { TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); sc = tempdecl->_scope; assert(sc); sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; sc->minst = minst; int needGagging = (gagged && !global.gag); unsigned int olderrors = global.errors; int oldGaggedErrors = -1; // dead-store to prevent spurious warning if (needGagging) oldGaggedErrors = global.startGagging(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic2(sc); if (gagged && global.errors != olderrors) break; } if (global.errors != olderrors) { if (!errors) { if (!tempdecl->literal) error(loc, "error instantiating"); if (tinst) tinst->printInstantiationTrace(); } errors = true; } if (needGagging) global.endGagging(oldGaggedErrors); sc = sc->pop(); sc->pop(); } } void TemplateInstance::semantic3(Scope *sc) { //if (toChars()[0] == 'D') *(char*)0=0; if (semanticRun >= PASSsemantic3) return; semanticRun = PASSsemantic3; if (!errors && members) { TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); sc = tempdecl->_scope; sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; sc->minst = minst; int needGagging = (gagged && !global.gag); unsigned int olderrors = global.errors; int oldGaggedErrors = -1; // dead-store to prevent spurious warning /* If this is a gagged instantiation, gag errors. * Future optimisation: If the results are actually needed, errors * would already be gagged, so we don't really need to run semantic * on the members. */ if (needGagging) oldGaggedErrors = global.startGagging(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic3(sc); if (gagged && global.errors != olderrors) break; } if (global.errors != olderrors) { if (!errors) { if (!tempdecl->literal) error(loc, "error instantiating"); if (tinst) tinst->printInstantiationTrace(); } errors = true; } if (needGagging) global.endGagging(oldGaggedErrors); sc = sc->pop(); sc->pop(); } } /************************************** * Given an error instantiating the TemplateInstance, * give the nested TemplateInstance instantiations that got * us here. Those are a list threaded into the nested scopes. */ void TemplateInstance::printInstantiationTrace() { if (global.gag) return; const unsigned max_shown = 6; const char format[] = "instantiated from here: %s"; // determine instantiation depth and number of recursive instantiations unsigned n_instantiations = 1; unsigned n_totalrecursions = 0; for (TemplateInstance *cur = this; cur; cur = cur->tinst) { ++n_instantiations; // If two instantiations use the same declaration, they are recursive. // (this works even if they are instantiated from different places in the // same template). // In principle, we could also check for multiple-template recursion, but it's // probably not worthwhile. if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl && cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc)) ++n_totalrecursions; } // show full trace only if it's short or verbose is on if (n_instantiations <= max_shown || global.params.verbose) { for (TemplateInstance *cur = this; cur; cur = cur->tinst) { cur->errors = true; errorSupplemental(cur->loc, format, cur->toChars()); } } else if (n_instantiations - n_totalrecursions <= max_shown) { // By collapsing recursive instantiations into a single line, // we can stay under the limit. int recursionDepth=0; for (TemplateInstance *cur = this; cur; cur = cur->tinst) { cur->errors = true; if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl && cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc)) { ++recursionDepth; } else { if (recursionDepth) errorSupplemental(cur->loc, "%d recursive instantiations from here: %s", recursionDepth+2, cur->toChars()); else errorSupplemental(cur->loc, format, cur->toChars()); recursionDepth = 0; } } } else { // Even after collapsing the recursions, the depth is too deep. // Just display the first few and last few instantiations. unsigned i = 0; for (TemplateInstance *cur = this; cur; cur = cur->tinst) { cur->errors = true; if (i == max_shown / 2) errorSupplemental(cur->loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown); if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2) errorSupplemental(cur->loc, format, cur->toChars()); ++i; } } } Dsymbol *TemplateInstance::toAlias() { if (!inst) { // Maybe we can resolve it if (_scope) { semantic(_scope); } if (!inst) { error("cannot resolve forward reference"); errors = true; return this; } } if (inst != this) return inst->toAlias(); if (aliasdecl) { return aliasdecl->toAlias(); } return inst; } const char *TemplateInstance::kind() const { return "template instance"; } bool TemplateInstance::oneMember(Dsymbol **ps, Identifier *) { *ps = NULL; return true; } const char *TemplateInstance::toChars() { OutBuffer buf; toCBufferInstance(this, &buf); return buf.extractString(); } const char *TemplateInstance::toPrettyCharsHelper() { OutBuffer buf; toCBufferInstance(this, &buf, true); return buf.extractString(); } /************************************* * Compare proposed template instantiation with existing template instantiation. * Note that this is not commutative because of the auto ref check. * Params: * this = proposed template instantiation * o = existing template instantiation * Returns: * 0 for match, 1 for no match */ int TemplateInstance::compare(RootObject *o) { TemplateInstance *ti = (TemplateInstance *)o; //printf("this = %p, ti = %p\n", this, ti); assert(tdtypes.dim == ti->tdtypes.dim); // Nesting must match if (enclosing != ti->enclosing) { //printf("test2 enclosing %s ti->enclosing %s\n", enclosing ? enclosing->toChars() : "", ti->enclosing ? ti->enclosing->toChars() : ""); goto Lnotequals; } //printf("parent = %s, ti->parent = %s\n", parent->toPrettyChars(), ti->parent->toPrettyChars()); if (!arrayObjectMatch(&tdtypes, &ti->tdtypes)) goto Lnotequals; /* Template functions may have different instantiations based on * "auto ref" parameters. */ if (FuncDeclaration *fd = ti->toAlias()->isFuncDeclaration()) { if (!fd->errors) { Parameters *fparameters = fd->getParameters(NULL); size_t nfparams = Parameter::dim(fparameters); // Num function parameters for (size_t j = 0; j < nfparams; j++) { Parameter *fparam = Parameter::getNth(fparameters, j); if (fparam->storageClass & STCautoref) // if "auto ref" { if (!fargs) goto Lnotequals; if (fargs->dim <= j) break; Expression *farg = (*fargs)[j]; if (farg->isLvalue()) { if (!(fparam->storageClass & STCref)) goto Lnotequals; // auto ref's don't match } else { if (fparam->storageClass & STCref) goto Lnotequals; // auto ref's don't match } } } } } return 0; Lnotequals: return 1; } hash_t TemplateInstance::toHash() { if (!hash) { hash = (size_t)(void *)enclosing; hash += arrayObjectHash(&tdtypes); hash += hash == 0; } return hash; } /************************************** * IsExpression can evaluate the specified type speculatively, and even if * it instantiates any symbols, they are normally unnecessary for the * final executable. * However, if those symbols leak to the actual code, compiler should remark * them as non-speculative to generate their code and link to the final executable. */ void unSpeculative(Scope *sc, RootObject *o) { if (!o) return; if (Tuple *tup = isTuple(o)) { for (size_t i = 0; i < tup->objects.dim; i++) { unSpeculative(sc, tup->objects[i]); } return; } Dsymbol *s = getDsymbol(o); if (!s) return; if (Declaration *d = s->isDeclaration()) { if (VarDeclaration *vd = d->isVarDeclaration()) o = vd->type; else if (AliasDeclaration *ad = d->isAliasDeclaration()) { o = ad->getType(); if (!o) o = ad->toAlias(); } else o = d->toAlias(); s = getDsymbol(o); if (!s) return; } if (TemplateInstance *ti = s->isTemplateInstance()) { // If the instance is already non-speculative, // or it is leaked to the speculative scope. if (ti->minst != NULL || sc->minst == NULL) return; // Remark as non-speculative instance. ti->minst = sc->minst; if (!ti->tinst) ti->tinst = sc->tinst; unSpeculative(sc, ti->tempdecl); } if (TemplateInstance *ti = s->isInstantiated()) unSpeculative(sc, ti); } /*********************************************** * Returns true if this is not instantiated in non-root module, and * is a part of non-speculative instantiatiation. * * Note: minst does not stabilize until semantic analysis is completed, * so don't call this function during semantic analysis to return precise result. */ bool TemplateInstance::needsCodegen() { // Now -allInst is just for the backward compatibility. if (global.params.allInst) { //printf("%s minst = %s, enclosing (%s)->isNonRoot = %d\n", // toPrettyChars(), minst ? minst->toChars() : NULL, // enclosing ? enclosing->toPrettyChars() : NULL, enclosing && enclosing->inNonRoot()); if (enclosing) { // Bugzilla 14588: If the captured context is not a function // (e.g. class), the instance layout determination is guaranteed, // because the semantic/semantic2 pass will be executed // even for non-root instances. if (!enclosing->isFuncDeclaration()) return true; // Bugzilla 14834: If the captured context is a function, // this excessive instantiation may cause ODR violation, because // -allInst and others doesn't guarantee the semantic3 execution // for that function. // If the enclosing is also an instantiated function, // we have to rely on the ancestor's needsCodegen() result. if (TemplateInstance *ti = enclosing->isInstantiated()) return ti->needsCodegen(); // Bugzilla 13415: If and only if the enclosing scope needs codegen, // this nested templates would also need code generation. return !enclosing->inNonRoot(); } return true; } if (!minst) { // If this is a speculative instantiation, // 1. do codegen if ancestors really needs codegen. // 2. become non-speculative if siblings are not speculative TemplateInstance *tnext = this->tnext; TemplateInstance *tinst = this->tinst; // At first, disconnect chain first to prevent infinite recursion. this->tnext = NULL; this->tinst = NULL; // Determine necessity of tinst before tnext. if (tinst && tinst->needsCodegen()) { minst = tinst->minst; // cache result assert(minst); assert(minst->isRoot() || minst->rootImports()); return true; } if (tnext && (tnext->needsCodegen() || tnext->minst)) { minst = tnext->minst; // cache result assert(minst); return minst->isRoot() || minst->rootImports(); } // Elide codegen because this is really speculative. return false; } /* Even when this is reached to the codegen pass, * a non-root nested template should not generate code, * due to avoid ODR violation. */ if (enclosing && enclosing->inNonRoot()) { if (tinst) { bool r = tinst->needsCodegen(); minst = tinst->minst; // cache result return r; } if (tnext) { bool r = tnext->needsCodegen(); minst = tnext->minst; // cache result return r; } return false; } /* The issue is that if the importee is compiled with a different -debug * setting than the importer, the importer may believe it exists * in the compiled importee when it does not, when the instantiation * is behind a conditional debug declaration. */ // workaround for Bugzilla 11239 if (global.params.useUnitTests || global.params.debuglevel) { // Prefer instantiations from root modules, to maximize link-ability. if (minst->isRoot()) return true; TemplateInstance *tnext = this->tnext; TemplateInstance *tinst = this->tinst; this->tnext = NULL; this->tinst = NULL; if (tinst && tinst->needsCodegen()) { minst = tinst->minst; // cache result assert(minst); assert(minst->isRoot() || minst->rootImports()); return true; } if (tnext && tnext->needsCodegen()) { minst = tnext->minst; // cache result assert(minst); assert(minst->isRoot() || minst->rootImports()); return true; } // Bugzilla 2500 case if (minst->rootImports()) return true; // Elide codegen because this is not included in root instances. return false; } else { // Prefer instantiations from non-root module, to minimize object code size. /* If a TemplateInstance is ever instantiated by non-root modules, * we do not have to generate code for it, * because it will be generated when the non-root module is compiled. * * But, if the non-root 'minst' imports any root modules, it might still need codegen. * * The problem is if A imports B, and B imports A, and both A * and B instantiate the same template, does the compilation of A * or the compilation of B do the actual instantiation? * * See Bugzilla 2500. */ if (!minst->isRoot() && !minst->rootImports()) return false; TemplateInstance *tnext = this->tnext; this->tnext = NULL; if (tnext && !tnext->needsCodegen() && tnext->minst) { minst = tnext->minst; // cache result assert(!minst->isRoot()); return false; } // Do codegen because this is not included in non-root instances. return true; } } /* ======================== TemplateMixin ================================ */ TemplateMixin::TemplateMixin(Loc loc, Identifier *ident, TypeQualified *tqual, Objects *tiargs) : TemplateInstance(loc, tqual->idents.dim ? (Identifier *)tqual->idents[tqual->idents.dim - 1] : ((TypeIdentifier *)tqual)->ident) { //printf("TemplateMixin(ident = '%s')\n", ident ? ident->toChars() : ""); this->ident = ident; this->tqual = tqual; this->tiargs = tiargs ? tiargs : new Objects(); } Dsymbol *TemplateMixin::syntaxCopy(Dsymbol *) { TemplateMixin *tm = new TemplateMixin(loc, ident, (TypeQualified *)tqual->syntaxCopy(), tiargs); return TemplateInstance::syntaxCopy(tm); } bool TemplateMixin::findTempDecl(Scope *sc) { // Follow qualifications to find the TemplateDeclaration if (!tempdecl) { Expression *e; Type *t; Dsymbol *s; tqual->resolve(loc, sc, &e, &t, &s); if (!s) { error("is not defined"); return false; } s = s->toAlias(); tempdecl = s->isTemplateDeclaration(); OverloadSet *os = s->isOverloadSet(); /* If an OverloadSet, look for a unique member that is a template declaration */ if (os) { Dsymbol *ds = NULL; for (size_t i = 0; i < os->a.dim; i++) { Dsymbol *s2 = os->a[i]->isTemplateDeclaration(); if (s2) { if (ds) { tempdecl = os; break; } ds = s2; } } } if (!tempdecl) { error("%s isn't a template", s->toChars()); return false; } } assert(tempdecl); struct ParamFwdResTm { static int fp(void *param, Dsymbol *s) { TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) return 0; TemplateMixin *tm = (TemplateMixin *)param; if (td->semanticRun == PASSinit) { if (td->_scope) td->semantic(td->_scope); else { tm->semanticRun = PASSinit; return 1; } } return 0; } }; // Look for forward references OverloadSet *tovers = tempdecl->isOverloadSet(); size_t overs_dim = tovers ? tovers->a.dim : 1; for (size_t oi = 0; oi < overs_dim; oi++) { if (overloadApply(tovers ? tovers->a[oi] : tempdecl, (void *)this, &ParamFwdResTm::fp)) return false; } return true; } void TemplateMixin::semantic(Scope *sc) { if (semanticRun != PASSinit) { // When a class/struct contains mixin members, and is done over // because of forward references, never reach here so semanticRun // has been reset to PASSinit. return; } semanticRun = PASSsemantic; Scope *scx = NULL; if (_scope) { sc = _scope; scx = _scope; // save so we don't make redundant copies _scope = NULL; } /* Run semantic on each argument, place results in tiargs[], * then find best match template with tiargs */ if (!findTempDecl(sc) || !semanticTiargs(sc) || !findBestMatch(sc, NULL)) { if (semanticRun == PASSinit) // forward reference had occured { //printf("forward reference - deferring\n"); _scope = scx ? scx : sc->copy(); _scope->setNoFree(); _scope->_module->addDeferredSemantic(this); return; } inst = this; errors = true; return; // error recovery } TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); if (!ident) { /* Assign scope local unique identifier, as same as lambdas. */ const char *s = "__mixin"; DsymbolTable *symtab; if (FuncDeclaration *func = sc->parent->isFuncDeclaration()) { symtab = func->localsymtab; if (symtab) { // Inside template constraint, symtab is not set yet. goto L1; } } else { symtab = sc->parent->isScopeDsymbol()->symtab; L1: assert(symtab); int num = (int)dmd_aaLen(symtab->tab) + 1; ident = Identifier::generateId(s, num); symtab->insert(this); } } 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 (size_t i = 0; i < tiargs->dim; i++) { RootObject *o = (*tiargs)[i]; Type *ta = isType(o); Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); RootObject *tmo = (*tm->tiargs)[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, Prot(PROTpublic)); break; } } Scope *scy = sc->push(this); scy->parent = this; argsym = new ScopeDsymbol(); argsym->parent = scy->parent; Scope *argscope = scy->push(argsym); unsigned errorsave = global.errors; // Declare each template parameter as an alias for the argument type declareParameters(argscope); // Add members to enclosing scope, as well as this scope for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->addMember(argscope, this); //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 Scope *sc2 = argscope->push(this); //size_t deferred_dim = Module::deferred.dim; static int nest; //printf("%d\n", nest); if (++nest > global.recursionLimit) { global.gag = 0; // ensure error message gets printed error("recursive expansion"); fatal(); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->setScope(sc2); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->importAll(sc2); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic(sc2); } nest--; /* In DeclDefs scope, TemplateMixin does not have to handle deferred symbols. * Because the members would already call Module::addDeferredSemantic() for themselves. * See Struct, Class, Interface, and EnumDeclaration::semantic(). */ //if (!sc->func && Module::deferred.dim > deferred_dim) {} AggregateDeclaration *ad = toParent()->isAggregateDeclaration(); if (sc->func && !ad) { semantic2(sc2); semantic3(sc2); } // Give additional context info if error occurred during instantiation if (global.errors != errorsave) { error("error instantiating"); errors = true; } sc2->pop(); argscope->pop(); scy->pop(); } void TemplateMixin::semantic2(Scope *sc) { if (semanticRun >= PASSsemantic2) return; semanticRun = PASSsemantic2; if (members) { assert(sc); sc = sc->push(argsym); sc = sc->push(this); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic2(sc); } sc = sc->pop(); sc->pop(); } } void TemplateMixin::semantic3(Scope *sc) { if (semanticRun >= PASSsemantic3) return; semanticRun = PASSsemantic3; if (members) { sc = sc->push(argsym); sc = sc->push(this); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic3(sc); } sc = sc->pop(); sc->pop(); } } const char *TemplateMixin::kind() const { return "mixin"; } bool TemplateMixin::oneMember(Dsymbol **ps, Identifier *ident) { return Dsymbol::oneMember(ps, ident); } int TemplateMixin::apply(Dsymbol_apply_ft_t fp, void *param) { if (_scope) // if fwd reference semantic(NULL); // try to resolve it if (members) { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; if (s) { if (s->apply(fp, param)) return 1; } } } return 0; } bool TemplateMixin::hasPointers() { //printf("TemplateMixin::hasPointers() %s\n", toChars()); if (members) { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; //printf(" s = %s %s\n", s->kind(), s->toChars()); if (s->hasPointers()) { return true; } } } return false; } void TemplateMixin::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion) { //printf("TemplateMixin::setFieldOffset() %s\n", toChars()); if (_scope) // if fwd reference semantic(NULL); // try to resolve it if (members) { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; //printf("\t%s\n", s->toChars()); s->setFieldOffset(ad, poffset, isunion); } } } const char *TemplateMixin::toChars() { OutBuffer buf; toCBufferInstance(this, &buf); return buf.extractString(); }