blob: 0173ee401b685c9a2b52602554e8014042c8eb31 [file] [log] [blame]
/* 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 = &pr; // 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 = &pr; // 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;