| |
| /* 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 "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<Expression *>(o); |
| if (!o || o->dyncast() != DYNCAST_EXPRESSION) |
| return NULL; |
| return (Expression *)o; |
| } |
| |
| Dsymbol *isDsymbol(RootObject *o) |
| { |
| //return dynamic_cast<Dsymbol *>(o); |
| if (!o || o->dyncast() != DYNCAST_DSYMBOL) |
| return NULL; |
| return (Dsymbol *)o; |
| } |
| |
| Type *isType(RootObject *o) |
| { |
| //return dynamic_cast<Type *>(o); |
| if (!o || o->dyncast() != DYNCAST_TYPE) |
| return NULL; |
| return (Type *)o; |
| } |
| |
| Tuple *isTuple(RootObject *o) |
| { |
| //return dynamic_cast<Tuple *>(o); |
| if (!o || o->dyncast() != DYNCAST_TUPLE) |
| return NULL; |
| return (Tuple *)o; |
| } |
| |
| Parameter *isParameter(RootObject *o) |
| { |
| //return dynamic_cast<Parameter *>(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 |
| */ |
| } |
| |
| 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; |
| |
| if (fd->semanticRun == PASSinit && fd->_scope) |
| { |
| Ungag ungag = fd->ungagSpeculative(); |
| fd->semantic(fd->_scope); |
| } |
| if (fd->semanticRun == PASSinit) |
| { |
| ::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; |
| |