blob: 20036f2dbff0b068ffa6c77b92b2bb7c9acd798b [file] [log] [blame]
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/template.c
*/
// Handle template implementation
#include "root/dsystem.h"
#include "root/root.h"
#include "root/aav.h"
#include "root/rmem.h"
#include "root/stringtable.h"
#include "root/hash.h"
#include "mangle.h"
#include "mtype.h"
#include "template.h"
#include "init.h"
#include "expression.h"
#include "scope.h"
#include "module.h"
#include "aggregate.h"
#include "declaration.h"
#include "dsymbol.h"
#include "mars.h"
#include "dsymbol.h"
#include "identifier.h"
#include "hdrgen.h"
#include "id.h"
#include "attrib.h"
#include "cond.h"
#include "tokens.h"
#define IDX_NOTFOUND (0x12345678) // index is not found
Type *rawTypeMerge(Type *t1, Type *t2);
bool MODimplicitConv(MOD modfrom, MOD modto);
MATCH MODmethodConv(MOD modfrom, MOD modto);
MOD MODmerge(MOD mod1, MOD mod2);
static size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters);
static int arrayObjectMatch(Objects *oa1, Objects *oa2);
static unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam);
static MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam);
bool reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0);
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->length; 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 (VarExp *ve = ea->isVarExp())
sa = ve->var;
else if (FuncExp *fe = ea->isFuncExp())
sa = fe->td ? (Dsymbol *)fe->td : (Dsymbol *)fe->fd;
else if (TemplateExp *te = ea->isTemplateExp())
sa = te->td;
else if (ScopeExp *se = ea->isScopeExp())
sa = se->sds;
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 ||
e->op == TOKdot)
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->length != oa2->length)
return 0;
for (size_t j = 0; j < oa1->length; 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->length; 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->length; 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->length; 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->length; 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->length; 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->isTrivialAliasSeq = false;
this->isTrivialAlias = false;
this->previous = NULL;
this->protection = Prot(Prot::undefined);
this->inuse = 0;
this->instances = NULL;
// Compute in advance for Ddoc's use
// Bugzilla 11153: ident could be NULL if parsing fails.
if (!members || !ident)
return;
Dsymbol *s;
if (!Dsymbol::oneMembers(members, &s, ident) || !s)
return;
onemember = s;
s->parent = this;
/* Set isTrivialAliasSeq if this fits the pattern:
* template AliasSeq(T...) { alias AliasSeq = T; }
* or set isTrivialAlias if this fits the pattern:
* template Alias(T) { alias Alias = qualifiers(T); }
*/
if (!(parameters && parameters->length == 1))
return;
AliasDeclaration *ad = s->isAliasDeclaration();
if (!ad || !ad->type)
return;
TypeIdentifier *ti = ad->type->isTypeIdentifier();
if (!ti || ti->idents.length != 0)
return;
if (TemplateTupleParameter *ttp = (*parameters)[0]->isTemplateTupleParameter())
{
if (ti->ident == ttp->ident && ti->mod == 0)
{
//printf("found isAliasSeq %s %s\n", s->toChars(), ad->type->toChars());
isTrivialAliasSeq = true;
}
}
else if (TemplateTypeParameter *ttp = (*parameters)[0]->isTemplateTypeParameter())
{
if (ti->ident == ttp->ident)
{
//printf("found isAlias %s %s\n", s->toChars(), ad->type->toChars());
isTrivialAlias = true;
}
}
}
Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *)
{
//printf("TemplateDeclaration::syntaxCopy()\n");
TemplateParameters *p = NULL;
if (parameters)
{
p = new TemplateParameters();
p->setDim(parameters->length);
for (size_t i = 0; i < p->length; i++)
(*p)[i] = (*parameters)[i]->syntaxCopy();
}
return new TemplateDeclaration(loc, ident, p,
constraint ? constraint->syntaxCopy() : NULL,
Dsymbol::arraySyntaxCopy(members), ismixin, literal);
}
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->parameterList.parameters;
VarArg fvarargs = tf->parameterList.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 == VARARGtypesafe && i + 1 == nfparams)
fparam->storageClass |= STCvariadic;
}
for (size_t i = 0; i < fparameters->length; 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;
dsymbolSemantic(v, 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->length;
dedtypes->zero();
if (errors)
return MATCHnomatch;
size_t parameters_dim = parameters->length;
int variadic = isVariadic() != NULL;
// If more arguments than parameters, no match
if (ti->tiargs->length > parameters_dim && !variadic)
{
return MATCHnomatch;
}
assert(dedtypes_dim == parameters_dim);
assert(dedtypes_dim >= ti->tiargs->length || 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);
inuse++;
m2 = tp->matchArg(ti->loc, paramscope, ti->tiargs, i, parameters, dedtypes, &sparam);
inuse--;
//printf("\tm2 = %d\n", m2);
if (m2 == MATCHnomatch)
{
goto Lnomatch;
}
if (m2 < m)
m = m2;
if (!flag)
dsymbolSemantic(sparam, 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->length);
(*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->parameterList.parameters->length; i++)
(*tf->parameterList.parameters)[i]->defaultArg = NULL;
tf->next = NULL;
// Resolve parameter types and 'auto ref's.
tf->fargs = fargs;
unsigned olderrors = global.startGagging();
fd->type = typeSemantic(tf, 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->length);
for (size_t i = 0; i < parameters->length; 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->length);
// 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.length; 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;
ParameterList fparameters; // function parameter list
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->length);
dedargs->zero();
dedtypes->setDim(parameters->length);
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->length;
size_t n = parameters->length;
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->length);
(*dedargs)[parameters->length - 1] = t;
t->objects.setDim(ntargs - n);
for (size_t i = 0; i < t->objects.length; 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->length);
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;
dsymbolSemantic(sparam, paramscope);
if (!paramscope->insert(sparam))
goto Lnomatch;
}
if (n < parameters->length && !declaredTuple)
{
inferStart = n;
}
else
inferStart = parameters->length;
//printf("tiargs matchTiargs = %d\n", matchTiargs);
}
fparameters = fd->getParameterList();
nfparams = fparameters.length(); // number of function parameters
nfargs = fargs ? fargs->length : 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->length - 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.parameters)[fptupindex];
if (fparam->type->ty != Tident)
continue;
TypeIdentifier *tid = (TypeIdentifier *)fparam->type;
if (!tp->ident->equals(tid->ident) || tid->idents.length)
continue;
if (fparameters.varargs != VARARGnone) // 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->length; 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.length : 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 = 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->length - 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 = fparameters[j];
if (!reliesOnTident(p->type, parameters, inferStart))
{
Type *pt = typeSemantic(p->type->syntaxCopy(), fd->loc, paramscope);
rem += pt->ty == Ttuple ? ((TypeTuple *)pt)->arguments->length : 1;
}
else
{
++rem;
}
}
if (nfargs2 - argi < rem)
goto Lnomatch;
declaredTuple->objects.setDim(nfargs2 - argi - rem);
for (size_t i = 0; i < declaredTuple->objects.length; 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.length; i++)
{
if (!isType(declaredTuple->objects[i]))
goto Lnomatch;
}
}
assert(declaredTuple);
argi += declaredTuple->objects.length;
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 = typeSemantic(prmtype->syntaxCopy(), fd->loc, paramscope);
if (prmtype->ty == Ttuple)
{
TypeTuple *tt = (TypeTuple *)prmtype;
size_t tt_dim = tt->arguments->length;
for (size_t j = 0; j < tt_dim; j++, ++argi)
{
Parameter *p = (*tt->arguments)[j];
if (j == tt_dim - 1 && fparameters.varargs == VARARGtypesafe &&
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->length; 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->length; 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
{
inuse++;
oded = tparam->defaultArg(instLoc, paramscope);
inuse--;
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 = expressionSemantic(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.length == 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->length);
}
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.length == 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 (fparameters.varargs == VARARGtypesafe && 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 (!(fparameters.varargs == VARARGtypesafe && 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 = typeSemantic(tvp->valType, 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 && fparameters.varargs == VARARGnone)
goto Lnomatch;
}
Lmatch:
for (size_t i = 0; i < dedtypes->length; 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->length; 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
{
inuse++;
oded = tparam->defaultArg(instLoc, paramscope);
inuse--;
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->length - 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->length)
{
if (Tuple *va = isTuple((*dedargs)[d - 1]))
{
if (va->objects.length)
{
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());
dsymbolSemantic(d, 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->length;
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.
* Params:
* 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
* pMessage = address to store error message, or null
*/
void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc,
Objects *tiargs, Type *tthis, Expressions *fargs, const char **pMessage)
{
struct ParamDeduce
{
// context
Loc loc;
Scope *sc;
Type *tthis;
Objects *tiargs;
Expressions *fargs;
const char **pMessage;
// 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->length > 0)
return 0;
// constructors need a valid scope in order to detect semantic errors
if (!fd->isCtorDeclaration() &&
fd->semanticRun < PASSsemanticdone)
{
Ungag ungag = fd->ungagSpeculative();
dsymbolSemantic(fd, NULL);
}
if (fd->semanticRun < PASSsemanticdone)
{
::error(loc, "forward reference to template %s", fd->toChars());
return 1;
}
//printf("fd = %s %s, fargs = %s\n", fd->toChars(), fd->type->toChars(), fargs->toChars());
m->anyf = fd;
TypeFunction *tf = (TypeFunction *)fd->type;
int prop = (tf->isproperty) ? 1 : 2;
if (property == 0)
property = prop;
else if (property != prop)
error(fd->loc, "cannot overload both property and non-property functions");
/* For constructors, qualifier check will be opposite direction.
* Qualified constructor always makes qualified object, then will be checked
* that it is implicitly convertible to tthis.
*/
Type *tthis_fd = fd->needThis() ? tthis : NULL;
bool isCtorCall = tthis_fd && fd->isCtorDeclaration();
if (isCtorCall)
{
//printf("%s tf->mod = x%x tthis_fd->mod = x%x %d\n", tf->toChars(),
// tf->mod, tthis_fd->mod, fd->isolateReturn());
if (MODimplicitConv(tf->mod, tthis_fd->mod) ||
(tf->isWild() && tf->isShared() == tthis_fd->isShared()) ||
fd->isolateReturn())
{
/* && tf->isShared() == tthis_fd->isShared()*/
// Uniquely constructed object can ignore shared qualifier.
// TODO: Is this appropriate?
tthis_fd = NULL;
}
else
return 0; // MATCHnomatch
}
MATCH mfa = tf->callMatch(tthis_fd, fargs, 0, pMessage);
//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");
if (td->inuse)
{
td->error(loc, "recursive template expansion");
return 1;
}
if (td == td_best) // skip duplicates
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();
dsymbolSemantic(td, 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->length);
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;
templateInstanceSemantic(ti, 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;
p.pMessage = pMessage;
// 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);
templateInstanceSemantic(ti, 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 = typeSemantic(tf, 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->length; 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->parameterList.parameters->length; i++)
(*tf->parameterList.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 = typeSemantic(fd->type, 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->length; 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->parameterList));
}
}
if (constraint)
{
buf.writestring(" if (");
::toCBuffer(constraint, &buf, &hgs);
buf.writeByte(')');
}
return buf.extractChars();
}
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->length; 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->length; 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->length; i++)
{
TemplateParameter *tp = (*parameters)[i];
if (tp->ident->equals(id))
return i;
}
return IDX_NOTFOUND;
}
size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters)
{
if (tparam->ty == Tident)
{
TypeIdentifier *tident = (TypeIdentifier *)tparam;
//printf("\ttident = '%s'\n", tident->toChars());
return templateIdentifierLookup(tident->ident, parameters);
}
return IDX_NOTFOUND;
}
unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam)
{
if ((tparam->mod & MODwild) == 0)
return 0;
*at = NULL;
#define X(U,T) ((U) << 4) | (T)
switch (X(tparam->mod, t->mod))
{
case X(MODwild, 0):
case X(MODwild, MODconst):
case X(MODwild, MODshared):
case X(MODwild, MODshared | MODconst):
case X(MODwild, MODimmutable):
case X(MODwildconst, 0):
case X(MODwildconst, MODconst):
case X(MODwildconst, MODshared):
case X(MODwildconst, MODshared | MODconst):
case X(MODwildconst, MODimmutable):
case X(MODshared | MODwild, MODshared):
case X(MODshared | MODwild, MODshared | MODconst):
case X(MODshared | MODwild, MODimmutable):
case X(MODshared | MODwildconst, MODshared):
case X(MODshared | MODwildconst, MODshared | MODconst):
case X(MODshared | MODwildconst, MODimmutable):
{
unsigned char wm = (t->mod & ~MODshared);
if (wm == 0)
wm = MODmutable;
unsigned char m = (t->mod & (MODconst | MODimmutable)) | (tparam->mod & t->mod & MODshared);
*at = t->unqualify(m);
return wm;
}
case X(MODwild, MODwild):
case X(MODwild, MODwildconst):
case X(MODwild, MODshared | MODwild):
case X(MODwild, MODshared | MODwildconst):
case X(MODwildconst, MODwild):
case X(MODwildconst, MODwildconst):
case X(MODwildconst, MODshared | MODwild):
case X(MODwildconst, MODshared | MODwildconst):
case X(MODshared | MODwild, MODshared | MODwild):
case X(MODshared | MODwild, MODshared | MODwildconst):
case X(MODshared | MODwildconst, MODshared | MODwild):
case X(MODshared | MODwildconst, MODshared | MODwildconst):
{
*at = t->unqualify(tparam->mod & t->mod);
return MODwild;
}
default:
return 0;
}
#undef X
}
MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam)
{
// 9*9 == 81 cases
#define X(U,T) ((U) << 4) | (T)
switch (X(tparam->mod, t->mod))
{
case X(0, 0):
case X(0, MODconst):
case X(0, MODwild):
case X(0, MODwildconst):
case X(0, MODshared):
case X(0, MODshared | MODconst):
case X(0, MODshared | MODwild):
case X(0, MODshared | MODwildconst):
case X(0, MODimmutable):
// foo(U) T => T
// foo(U) const(T) => const(T)
// foo(U) inout(T) => inout(T)
// foo(U) inout(const(T)) => inout(const(T))
// foo(U) shared(T) => shared(T)
// foo(U) shared(const(T)) => shared(const(T))
// foo(U) shared(inout(T)) => shared(inout(T))
// foo(U) shared(inout(const(T))) => shared(inout(const(T)))
// foo(U) immutable(T) => immutable(T)
{
*at = t;
return MATCHexact;
}
case X(MODconst, MODconst):
case X(MODwild, MODwild):
case X(MODwildconst, MODwildconst):
case X(MODshared, MODshared):
case X(MODshared | MODconst, MODshared | MODconst):
case X(MODshared | MODwild, MODshared | MODwild):
case X(MODshared | MODwildconst, MODshared | MODwildconst):
case X(MODimmutable, MODimmutable):
// foo(const(U)) const(T) => T
// foo(inout(U)) inout(T) => T
// foo(inout(const(U))) inout(const(T)) => T
// foo(shared(U)) shared(T) => T
// foo(shared(const(U))) shared(const(T)) => T
// foo(shared(inout(U))) shared(inout(T)) => T
// foo(shared(inout(const(U)))) shared(inout(const(T))) => T
// foo(immutable(U)) immutable(T) => T
{
*at = t->mutableOf()->unSharedOf();
return MATCHexact;
}
case X(MODconst, 0):
case X(MODconst, MODwild):
case X(MODconst, MODwildconst):
case X(MODconst, MODshared | MODconst):
case X(MODconst, MODshared | MODwild):
case X(MODconst, MODshared | MODwildconst):
case X(MODconst, MODimmutable):
case X(MODwild, MODshared | MODwild):
case X(MODwildconst, MODshared | MODwildconst):
case X(MODshared | MODconst, MODimmutable):
// foo(const(U)) T => T
// foo(const(U)) inout(T) => T
// foo(const(U)) inout(const(T)) => T
// foo(const(U)) shared(const(T)) => shared(T)
// foo(const(U)) shared(inout(T)) => shared(T)
// foo(const(U)) shared(inout(const(T))) => shared(T)
// foo(const(U)) immutable(T) => T
// foo(inout(U)) shared(inout(T)) => shared(T)
// foo(inout(const(U))) shared(inout(const(T))) => shared(T)
// foo(shared(const(U))) immutable(T) => T
{
*at = t->mutableOf();
return MATCHconst;
}
case X(MODconst, MODshared):
// foo(const(U)) shared(T) => shared(T)
{
*at = t;
return MATCHconst;
}
case X(MODshared, MODshared | MODconst):
case X(MODshared, MODshared | MODwild):
case X(MODshared, MODshared | MODwildconst):
case X(MODshared | MODconst, MODshared):
// foo(shared(U)) shared(const(T)) => const(T)
// foo(shared(U)) shared(inout(T)) => inout(T)
// foo(shared(U)) shared(inout(const(T))) => inout(const(T))
// foo(shared(const(U))) shared(T) => T
{
*at = t->unSharedOf();
return MATCHconst;
}
case X(MODwildconst, MODimmutable):
case X(MODshared | MODconst, MODshared | MODwildconst):
case X(MODshared | MODwildconst, MODimmutable):
case X(MODshared | MODwildconst, MODshared | MODwild):
// foo(inout(const(U))) immutable(T) => T
// foo(shared(const(U))) shared(inout(const(T))) => T
// foo(shared(inout(const(U)))) immutable(T) => T
// foo(shared(inout(const(U)))) shared(inout(T)) => T
{
*at = t->unSharedOf()->mutableOf();
return MATCHconst;
}
case X(MODshared | MODconst, MODshared | MODwild):
// foo(shared(const(U))) shared(inout(T)) => T
{
*at = t->unSharedOf()->mutableOf();
return MATCHconst;
}
case X(MODwild, 0):
case X(MODwild, MODconst):
case X(MODwild, MODwildconst):
case X(MODwild, MODimmutable):
case X(MODwild, MODshared):
case X(MODwild, MODshared | MODconst):
case X(MODwild, MODshared | MODwildconst):
case X(MODwildconst, 0):
case X(MODwildconst, MODconst):
case X(MODwildconst, MODwild):
case X(MODwildconst, MODshared):
case X(MODwildconst, MODshared | MODconst):
case X(MODwildconst, MODshared | MODwild):
case X(MODshared, 0):
case X(MODshared, MODconst):
case X(MODshared, MODwild):
case X(MODshared, MODwildconst):
case X(MODshared, MODimmutable):
case X(MODshared | MODconst, 0):
case X(MODshared | MODconst, MODconst):
case X(MODshared | MODconst, MODwild):
case X(MODshared | MODconst, MODwildconst):
case X(MODshared | MODwild, 0):
case X(MODshared | MODwild, MODconst):
case X(MODshared | MODwild, MODwild):
case X(MODshared | MODwild, MODwildconst):
case X(MODshared | MODwild, MODimmutable):
case X(MODshared | MODwild, MODshared):
case X(MODshared | MODwild, MODshared | MODconst):
case X(MODshared | MODwild, MODshared | MODwildconst):
case X(MODshared | MODwildconst, 0):
case X(MODshared | MODwildconst, MODconst):
case X(MODshared | MODwildconst, MODwild):
case X(MODshared | MODwildconst, MODwildconst):
case X(MODshared | MODwildconst, MODshared):
case X(MODshared | MODwildconst, MODshared | MODconst):
case X(MODimmutable, 0):
case X(MODimmutable, MODconst):
case X(MODimmutable, MODwild):
case X(MODimmutable, MODwildconst):
case X(MODimmutable, MODshared):
case X(MODimmutable, MODshared | MODconst):
case X(MODimmutable, MODshared | MODwild):
case X(MODimmutable, MODshared | MODwildconst):
// foo(inout(U)) T => nomatch
// foo(inout(U)) const(T) => nomatch
// foo(inout(U)) inout(const(T)) => nomatch
// foo(inout(U)) immutable(T) => nomatch
// foo(inout(U)) shared(T) => nomatch
// foo(inout(U)) shared(const(T)) => nomatch
// foo(inout(U)) shared(inout(const(T))) => nomatch
// foo(inout(const(U))) T => nomatch
// foo(inout(const(U))) const(T) => nomatch
// foo(inout(const(U))) inout(T) => nomatch
// foo(inout(const(U))) shared(T) => nomatch
// foo(inout(const(U))) shared(const(T)) => nomatch
// foo(inout(const(U))) shared(inout(T)) => nomatch
// foo(shared(U)) T => nomatch
// foo(shared(U)) const(T) => nomatch
// foo(shared(U)) inout(T) => nomatch
// foo(shared(U)) inout(const(T)) => nomatch
// foo(shared(U)) immutable(T) => nomatch
// foo(shared(const(U))) T => nomatch
// foo(shared(const(U))) const(T) => nomatch
// foo(shared(const(U))) inout(T) => nomatch
// foo(shared(const(U))) inout(const(T)) => nomatch
// foo(shared(inout(U))) T => nomatch
// foo(shared(inout(U))) const(T) => nomatch
// foo(shared(inout(U))) inout(T) => nomatch
// foo(shared(inout(U))) inout(const(T)) => nomatch
// foo(shared(inout(U))) immutable(T) => nomatch
// foo(shared(inout(U))) shared(T) => nomatch
// foo(shared(inout(U))) shared(const(T)) => nomatch
// foo(shared(inout(U))) shared(inout(const(T))) => nomatch
// foo(shared(inout(const(U)))) T => nomatch
// foo(shared(inout(const(U)))) const(T) => nomatch
// foo(shared(inout(const(U)))) inout(T) => nomatch
// foo(shared(inout(const(U)))) inout(const(T)) => nomatch
// foo(shared(inout(const(U)))) shared(T) => nomatch
// foo(shared(inout(const(U)))) shared(const(T)) => nomatch
// foo(immutable(U)) T => nomatch
// foo(immutable(U)) const(T) => nomatch
// foo(immutable(U)) inout(T) => nomatch
// foo(immutable(U)) inout(const(T)) => nomatch
// foo(immutable(U)) shared(T) => nomatch
// foo(immutable(U)) shared(const(T)) => nomatch
// foo(immutable(U)) shared(inout(T)) => nomatch
// foo(immutable(U)) shared(inout(const(T))) => nomatch
return MATCHnomatch;
default:
assert(0);
return MATCHnomatch; // silence compiler warning about missing return
}
#undef X
}
/* These form the heart of template argument deduction.
* Given 'this' being the type argument to the template instance,
* it is matched against the template declaration parameter specialization
* 'tparam' to determine the type to be used for the parameter.
* Example:
* template Foo(T:T*) // template declaration
* Foo!(int*) // template instantiation
* Input:
* this = int*
* tparam = T*
* parameters = [ T:T* ] // Array of TemplateParameter's
* Output:
* dedtypes = [ int ] // Array of Expression/Type's
*/
MATCH deduceType(RootObject *o, Scope *sc, Type *tparam, TemplateParameters *parameters,
Objects *dedtypes, unsigned *wm, size_t inferStart)
{
class DeduceType : public Visitor
{
public:
Scope *sc;
Type *tparam;
TemplateParameters *parameters;
Objects *dedtypes;
unsigned *wm;
size_t inferStart;
MATCH result;
DeduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm, size_t inferStart)
: sc(sc), tparam(tparam), parameters(parameters), dedtypes(dedtypes), wm(wm), inferStart(inferStart)
{
result = MATCHnomatch;
}
void visit(Type *t)
{
if (!tparam)
goto Lnomatch;
if (t == tparam)
goto Lexact;
if (tparam->ty == Tident)
{
// Determine which parameter tparam is
size_t i = templateParameterLookup(tparam, parameters);
if (i == IDX_NOTFOUND)
{
if (!sc)
goto Lnomatch;
/* Need a loc to go with the semantic routine.
*/
Loc loc;
if (parameters->length)
{
TemplateParameter *tp = (*parameters)[0];
loc = tp->loc;
}
/* BUG: what if tparam is a template instance, that
* has as an argument another Tident?
*/
tparam = typeSemantic(tparam, loc, sc);
assert(tparam->ty != Tident);
result = deduceType(t, sc, tparam, parameters, dedtypes, wm);
return;
}
TemplateParameter *tp = (*parameters)[i];
TypeIdentifier *tident = (TypeIdentifier *)tparam;
if (tident->idents.length > 0)
{
//printf("matching %s to %s\n", tparam->toChars(), t->toChars());
Dsymbol *s = t->toDsymbol(sc);
for (size_t j = tident->idents.length; j-- > 0; )
{
RootObject *id = tident->idents[j];
if (id->dyncast() == DYNCAST_IDENTIFIER)
{
if (!s || !s->parent)
goto Lnomatch;
Dsymbol *s2 = s->parent->search(Loc(), (Identifier *)id);
if (!s2)
goto Lnomatch;
s2 = s2->toAlias();
//printf("[%d] s = %s %s, s2 = %s %s\n", j, s->kind(), s->toChars(), s2->kind(), s2->toChars());
if (s != s2)
{
if (Type *tx = s2->getType())
{
if (s != tx->toDsymbol(sc))
goto Lnomatch;
}
else
goto Lnomatch;
}
s = s->parent;
}
else
goto Lnomatch;
}
//printf("[e] s = %s\n", s?s->toChars():"(null)");
if (tp->isTemplateTypeParameter())
{
Type *tt = s->getType();
if (!tt)
goto Lnomatch;
Type *at = (Type *)(*dedtypes)[i];
if (at && at->ty == Tnone)
at = ((TypeDeduced *)at)->tded;
if (!at || tt->equals(at))
{
(*dedtypes)[i] = tt;
goto Lexact;
}
}
if (tp->isTemplateAliasParameter())
{
Dsymbol *s2 = (Dsymbol *)(*dedtypes)[i];
if (!s2 || s == s2)
{
(*dedtypes)[i] = s;
goto Lexact;
}
}
goto Lnomatch;
}
// Found the corresponding parameter tp
if (!tp->isTemplateTypeParameter())
goto Lnomatch;
Type *at = (Type *)(*dedtypes)[i];
Type *tt;
if (unsigned char wx = wm ? deduceWildHelper(t, &tt, tparam) : 0)
{
// type vs (none)
if (!at)
{
(*dedtypes)[i] = tt;
*wm |= wx;
result = MATCHconst;
return;
}
// type vs expressions
if (at->ty == Tnone)
{
TypeDeduced *xt = (TypeDeduced *)at;
result = xt->matchAll(tt);
if (result > MATCHnomatch)
{
(*dedtypes)[i] = tt;
if (result > MATCHconst)
result = MATCHconst; // limit level for inout matches
delete xt;
}
return;
}
// type vs type
if (tt->equals(at))
{
(*dedtypes)[i] = tt; // Prefer current type match
goto Lconst;
}
if (tt->implicitConvTo(at->constOf()))
{
(*dedtypes)[i] = at->constOf()->mutableOf();
*wm |= MODconst;
goto Lconst;
}
if (at->implicitConvTo(tt->constOf()))
{
(*dedtypes)[i] = tt->constOf()->mutableOf();
*wm |= MODconst;
goto Lconst;
}
goto Lnomatch;
}
else if (MATCH m = deduceTypeHelper(t, &tt, tparam))
{
// type vs (none)
if (!at)
{
(*dedtypes)[i] = tt;
result = m;
return;
}
// type vs expressions
if (at->ty == Tnone)
{
TypeDeduced *xt = (TypeDeduced *)at;
result = xt->matchAll(tt);
if (result > MATCHnomatch)
{
(*dedtypes)[i] = tt;
delete xt;
}
return;
}
// type vs type
if (tt->equals(at))
{
goto Lexact;
}
if (tt->ty == Tclass && at->ty == Tclass)
{
result = tt->implicitConvTo(at);
return;
}
if (tt->ty == Tsarray && at->ty == Tarray &&
tt->nextOf()->implicitConvTo(at->nextOf()) >= MATCHconst)
{
goto Lexact;
}
}
goto Lnomatch;
}
if (tparam->ty == Ttypeof)
{
/* Need a loc to go with the semantic routine.
*/
Loc loc;
if (parameters->length)
{
TemplateParameter *tp = (*parameters)[0];
loc = tp->loc;
}
tparam = typeSemantic(tparam, loc, sc);
}
if (t->ty != tparam->ty)
{
if (Dsymbol *sym = t->toDsymbol(sc))
{
if (sym->isforwardRef() && !tparam->deco)
goto Lnomatch;
}
MATCH m = t->implicitConvTo(tparam);
if (m == MATCHnomatch)
{
if (t->ty == Tclass)
{
TypeClass *tc = (TypeClass *)t;
if (tc->sym->aliasthis && !(tc->att & RECtracingDT))
{
tc->att = (AliasThisRec)(tc->att | RECtracingDT);
m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm);
tc->att = (AliasThisRec)(tc->att & ~RECtracingDT);
}
}
else if (t->ty == Tstruct)
{
TypeStruct *ts = (TypeStruct *)t;
if (ts->sym->aliasthis && !(ts->att & RECtracingDT))
{
ts->att = (AliasThisRec)(ts->att | RECtracingDT);
m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm);
ts->att = (AliasThisRec)(ts->att & ~RECtracingDT);
}
}
}
result = m;
return;
}
if (t->nextOf())
{
if (tparam->deco && !tparam->hasWild())
{
result = t->implicitConvTo(tparam);
return;
}
Type *tpn = tparam->nextOf();
if (wm && t->ty == Taarray && tparam->isWild())
{
// Bugzilla 12403: In IFTI, stop inout matching on transitive part of AA types.
tpn = tpn->substWildTo(MODmutable);
}
result = deduceType(t->nextOf(), sc, tpn, parameters, dedtypes, wm);
return;
}
Lexact:
result = MATCHexact;
return;
Lnomatch:
result = MATCHnomatch;
return;
Lconst:
result = MATCHconst;
}
void visit(TypeVector *t)
{
if (tparam->ty == Tvector)
{
TypeVector *tp = (TypeVector *)tparam;
result = deduceType(t->basetype, sc, tp->basetype, parameters, dedtypes, wm);
return;
}
visit((Type *)t);
}
void visit(TypeDArray *t)
{
visit((Type *)t);
}
void visit(TypeSArray *t)
{
// Extra check that array dimensions must match
if (tparam)
{
if (tparam->ty == Tarray)
{
MATCH m = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm);
result = (m >= MATCHconst) ? MATCHconvert : MATCHnomatch;
return;
}
TemplateParameter *tp = NULL;
Expression *edim = NULL;
size_t i;
if (tparam->ty == Tsarray)
{
TypeSArray *tsa = (TypeSArray *)tparam;
if (tsa->dim->op == TOKvar &&
((VarExp *)tsa->dim)->var->storage_class & STCtemplateparameter)
{
Identifier *id = ((VarExp *)tsa->dim)->var->ident;
i = templateIdentifierLookup(id, parameters);
assert(i != IDX_NOTFOUND);
tp = (*parameters)[i];
}
else
edim = tsa->dim;
}
else if (tparam->ty == Taarray)
{
TypeAArray *taa = (TypeAArray *)tparam;
i = templateParameterLookup(taa->index, parameters);
if (i != IDX_NOTFOUND)
tp = (*parameters)[i];
else
{
Expression *e;
Type *tx;
Dsymbol *s;
taa->index->resolve(Loc(), sc, &e, &tx, &s);
edim = s ? getValue(s) : getValue(e);
}
}
if ((tp && tp->matchArg(sc, t->dim, i, parameters, dedtypes, NULL)) ||
(edim && edim->toInteger() == t->dim->toInteger()))
{
result = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm);
return;
}
}
visit((Type *)t);
return;
result = MATCHnomatch;
}
void visit(TypeAArray *t)
{
// Extra check that index type must match
if (tparam && tparam->ty == Taarray)
{
TypeAArray *tp = (TypeAArray *)tparam;
if (!deduceType(t->index, sc, tp->index, parameters, dedtypes))
{
result = MATCHnomatch;
return;
}
}
visit((Type *)t);
}
void visit(TypeFunction *t)
{
//printf("TypeFunction::deduceType()\n");
//printf("\tthis = %d, ", t->ty); t->print();
//printf("\ttparam = %d, ", tparam->ty); tparam->print();
// Extra check that function characteristics must match
if (tparam && tparam->ty == Tfunction)
{
TypeFunction *tp = (TypeFunction *)tparam;
if (t->parameterList.varargs != tp->parameterList.varargs ||
t->linkage != tp->linkage)
{
result = MATCHnomatch;
return;
}
size_t nfargs = t->parameterList.length();
size_t nfparams = tp->parameterList.length();
// bug 2579 fix: Apply function parameter storage classes to parameter types
for (size_t i = 0; i < nfparams; i++)
{
Parameter *fparam = tp->parameterList[i];
fparam->type = fparam->type->addStorageClass(fparam->storageClass);
fparam->storageClass &= ~(STC_TYPECTOR | STCin);
}
//printf("\t-> this = %d, ", t->ty); t->print();
//printf("\t-> tparam = %d, ", tparam->ty); tparam->print();
/* See if tuple match
*/
if (nfparams > 0 && nfargs >= nfparams - 1)
{
/* See if 'A' of the template parameter matches 'A'
* of the type of the last function parameter.
*/
Parameter *fparam = tp->parameterList[nfparams - 1];
assert(fparam);
assert(fparam->type);
if (fparam->type->ty != Tident)
goto L1;
TypeIdentifier *tid = (TypeIdentifier *)fparam->type;
if (tid->idents.length)
goto L1;
/* Look through parameters to find tuple matching tid->ident
*/
size_t tupi = 0;
for (; 1; tupi++)
{
if (tupi == parameters->length)
goto L1;
TemplateParameter *tx = (*parameters)[tupi];
TemplateTupleParameter *tup = tx->isTemplateTupleParameter();
if (tup && tup->ident->equals(tid->ident))
break;
}
/* The types of the function arguments [nfparams - 1 .. nfargs]
* now form the tuple argument.
*/
size_t tuple_dim = nfargs - (nfparams - 1);
/* See if existing tuple, and whether it matches or not
*/
RootObject *o = (*dedtypes)[tupi];
if (o)
{
// Existing deduced argument must be a tuple, and must match
Tuple *tup = isTuple(o);
if (!tup || tup->objects.length != tuple_dim)
{
result = MATCHnomatch;
return;
}
for (size_t i = 0; i < tuple_dim; i++)
{
Parameter *arg = t->parameterList[nfparams - 1 + i];
if (!arg->type->equals(tup->objects[i]))
{
result = MATCHnomatch;
return;
}
}
}
else
{
// Create new tuple
Tuple *tup = new Tuple();
tup->objects.setDim(tuple_dim);
for (size_t i = 0; i < tuple_dim; i++)
{
Parameter *arg = t->parameterList[nfparams - 1 + i];
tup->objects[i] = arg->type;
}
(*dedtypes)[tupi] = tup;
}
nfparams--; // don't consider the last parameter for type deduction
goto L2;
}
L1:
if (nfargs != nfparams)
{
result = MATCHnomatch;
return;
}
L2:
for (size_t i = 0; i < nfparams; i++)
{
Parameter *a = t->parameterList[i];
Parameter *ap = tp->parameterList[i];
if (!a->isCovariant(t->isref, ap) ||
!deduceType(a->type, sc, ap->type, parameters, dedtypes))
{
result = MATCHnomatch;
return;
}
}
}
visit((Type *)t);
}
void visit(TypeIdentifier *t)
{
// Extra check
if (tparam && tparam->ty == Tident)
{
TypeIdentifier *tp = (TypeIdentifier *)tparam;
for (size_t i = 0; i < t->idents.length; i++)
{
RootObject *id1 = t->idents[i];
RootObject *id2 = tp->idents[i];
if (!id1->equals(id2))
{
result = MATCHnomatch;
return;
}
}
}
visit((Type *)t);
}
void visit(TypeInstance *t)
{
// Extra check
if (tparam && tparam->ty == Tinstance && t->tempinst->tempdecl)
{
TemplateDeclaration *tempdecl = t->tempinst->tempdecl->isTemplateDeclaration();
assert(tempdecl);
TypeInstance *tp = (TypeInstance *)tparam;
//printf("tempinst->tempdecl = %p\n", tempdecl);
//printf("tp->tempinst->tempdecl = %p\n", tp->tempinst->tempdecl);
if (!tp->tempinst->tempdecl)
{
//printf("tp->tempinst->name = '%s'\n", tp->tempinst->name->toChars());
/* Handle case of:
* template Foo(T : sa!(T), alias sa)
*/
size_t i = templateIdentifierLookup(tp->tempinst->name, parameters);
if (i == IDX_NOTFOUND)
{
/* Didn't find it as a parameter identifier. Try looking
* it up and seeing if is an alias. See Bugzilla 1454
*/
TypeIdentifier *tid = new TypeIdentifier(tp->loc, tp->tempinst->name);
Type *tx;
Expression *e;
Dsymbol *s;
tid->resolve(tp->loc, sc, &e, &tx, &s);
if (tx)
{
s = tx->toDsymbol(sc);
if (TemplateInstance *ti = s ? s->parent->isTemplateInstance() : NULL)
{
// Bugzilla 14290: Try to match with ti->tempecl,
// only when ti is an enclosing instance.
Dsymbol *p = sc->parent;
while (p && p != ti)
p = p->parent;
if (p)
s = ti->tempdecl;
}
}
if (s)
{
s = s->toAlias();
TemplateDeclaration *td = s->isTemplateDeclaration();
if (td)
{
if (td->overroot)
td = td->overroot;
for (; td; td = td->overnext)
{
if (td == tempdecl)
goto L2;
}
}
}
goto Lnomatch;
}
TemplateParameter *tpx = (*parameters)[i];
if (!tpx->matchArg(sc, tempdecl, i, parameters, dedtypes, NULL))
goto Lnomatch;
}
else if (tempdecl != tp->tempinst->tempdecl)
goto Lnomatch;
L2:
for (size_t i = 0; 1; i++)
{
//printf("\ttest: tempinst->tiargs[%d]\n", i);
RootObject *o1 = NULL;
if (i < t->tempinst->tiargs->length)
o1 = (*t->tempinst->tiargs)[i];
else if (i < t->tempinst->tdtypes.length && i < tp->tempinst->tiargs->length)
{
// Pick up default arg
o1 = t->tempinst->tdtypes[i];
}
else if (i >= tp->tempinst->tiargs->length)
break;
if (i >= tp->tempinst->tiargs->length)
{
size_t dim = tempdecl->parameters->length - (tempdecl->isVariadic() ? 1 : 0);
while (i < dim && ((*tempdecl->parameters)[i]->dependent ||
(*tempdecl->parameters)[i]->hasDefaultArg()))
{
i++;
}
if (i >= dim)
break; // match if all remained parameters are dependent
goto Lnomatch;
}
RootObject *o2 = (*tp->tempinst->tiargs)[i];
Type *t2 = isType(o2);
size_t j = (t2 && t2->ty == Tident && i == tp->tempinst->tiargs->length - 1)
? templateParameterLookup(t2, parameters) : IDX_NOTFOUND;
if (j != IDX_NOTFOUND && j == parameters->length - 1 &&
(*parameters)[j]->isTemplateTupleParameter())
{
/* Given:
* struct A(B...) {}
* alias A!(int, float) X;
* static if (is(X Y == A!(Z), Z...)) {}
* deduce that Z is a tuple(int, float)
*/
/* Create tuple from remaining args
*/
Tuple *vt = new Tuple();
size_t vtdim = (tempdecl->isVariadic()
? t->tempinst->tiargs->length : t->tempinst->tdtypes.length) - i;
vt->objects.setDim(vtdim);
for (size_t k = 0; k < vtdim; k++)
{
RootObject *o;
if (k < t->tempinst->tiargs->length)
o = (*t->tempinst->tiargs)[i + k];
else // Pick up default arg
o = t->tempinst->tdtypes[i + k];
vt->objects[k] = o;
}
Tuple *v = (Tuple *)(*dedtypes)[j];
if (v)
{
if (!match(v, vt))
goto Lnomatch;
}
else
(*dedtypes)[j] = vt;
break;
}
else if (!o1)
break;
Type *t1 = isType(o1);
Dsymbol *s1 = isDsymbol(o1);
Dsymbol *s2 = isDsymbol(o2);
Expression *e1 = s1 ? getValue(s1) : getValue(isExpression(o1));
Expression *e2 = isExpression(o2);
if (t1 && t2)
{
if (!deduceType(t1, sc, t2, parameters, dedtypes))
goto Lnomatch;
}
else if (e1 && e2)
{
Le:
e1 = e1->ctfeInterpret();
/* If it is one of the template parameters for this template,
* we should not attempt to interpret it. It already has a value.
*/
if (e2->op == TOKvar &&
(((VarExp *)e2)->var->storage_class & STCtemplateparameter))
{
/*
* (T:Number!(e2), int e2)
*/
j = templateIdentifierLookup(((VarExp *)e2)->var->ident, parameters);
if (j != IDX_NOTFOUND)
goto L1;
// The template parameter was not from this template
// (it may be from a parent template, for example)
}
e2 = expressionSemantic(e2, sc); // Bugzilla 13417
e2 = e2->ctfeInterpret();
//printf("e1 = %s, type = %s %d\n", e1->toChars(), e1->type->toChars(), e1->type->ty);
//printf("e2 = %s, type = %s %d\n", e2->toChars(), e2->type->toChars(), e2->type->ty);
if (!e1->equals(e2))
{
if (!e2->implicitConvTo(e1->type))
goto Lnomatch;
e2 = e2->implicitCastTo(sc, e1->type);
e2 = e2->ctfeInterpret();
if (!e1->equals(e2))
goto Lnomatch;
}
}
else if (e1 && t2 && t2->ty == Tident)
{
j = templateParameterLookup(t2, parameters);
L1:
if (j == IDX_NOTFOUND)
{
t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2);
if (e2)
goto Le;
goto Lnomatch;
}
if (!(*parameters)[j]->matchArg(sc, e1, j, parameters, dedtypes, NULL))
goto Lnomatch;
}
else if (s1 && s2)
{
Ls:
if (!s1->equals(s2))
goto Lnomatch;
}
else if (s1 && t2 && t2->ty == Tident)
{
j = templateParameterLookup(t2, parameters);
if (j == IDX_NOTFOUND)
{
t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2);
if (s2)
goto Ls;
goto Lnomatch;
}
if (!(*parameters)[j]->matchArg(sc, s1, j, parameters, dedtypes, NULL))
goto Lnomatch;
}
else
goto Lnomatch;
}
}
visit((Type *)t);
return;
Lnomatch:
//printf("no match\n");
result = MATCHnomatch;
}
void visit(TypeStruct *t)
{
/* If this struct is a template struct, and we're matching
* it against a template instance, convert the struct type
* to a template instance, too, and try again.
*/
TemplateInstance *ti = t->sym->parent->isTemplateInstance();
if (tparam && tparam->ty == Tinstance)
{
if (ti && ti->toAlias() == t->sym)
{
TypeInstance *tx = new TypeInstance(Loc(), ti);
result = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
return;
}
/* Match things like:
* S!(T).foo
*/
TypeInstance *tpi = (TypeInstance *)tparam;
if (tpi->idents.length)
{
RootObject *id = tpi->idents[tpi->idents.length - 1];
if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id))
{
Type *tparent = t->sym->parent->getType();
if (tparent)
{
/* Slice off the .foo in S!(T).foo
*/
tpi->idents.length--;
result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
tpi->idents.length++;
return;
}
}
}
}
// Extra check
if (tparam && tparam->ty == Tstruct)
{
TypeStruct *tp = (TypeStruct *)tparam;
//printf("\t%d\n", (MATCH) t->implicitConvTo(tp));
if (wm && t->deduceWild(tparam, false))
{
result = MATCHconst;
return;
}
result = t->implicitConvTo(tp);
return;
}
visit((Type *)t);
}
void visit(TypeEnum *t)
{
// Extra check
if (tparam && tparam->ty == Tenum)
{
TypeEnum *tp = (TypeEnum *)tparam;
if (t->sym == tp->sym)
visit((Type *)t);
else
result = MATCHnomatch;
return;
}
Type *tb = t->toBasetype();
if (tb->ty == tparam->ty ||
(tb->ty == Tsarray && tparam->ty == Taarray))
{
result = deduceType(tb, sc, tparam, parameters, dedtypes, wm);
return;
}
visit((Type *)t);
}
/* Helper for TypeClass::deduceType().
* Classes can match with implicit conversion to a base class or interface.
* This is complicated, because there may be more than one base class which
* matches. In such cases, one or more parameters remain ambiguous.
* For example,
*
* interface I(X, Y) {}
* class C : I(uint, double), I(char, double) {}
* C x;
* foo(T, U)( I!(T, U) x)
*
* deduces that U is double, but T remains ambiguous (could be char or uint).
*
* Given a baseclass b, and initial deduced types 'dedtypes', this function
* tries to match tparam with b, and also tries all base interfaces of b.
* If a match occurs, numBaseClassMatches is incremented, and the new deduced
* types are ANDed with the current 'best' estimate for dedtypes.
*/
static void deduceBaseClassParameters(BaseClass *b,
Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes,
Objects *best, int &numBaseClassMatches)
{
TemplateInstance *parti = b->sym ? b->sym->parent->isTemplateInstance() : NULL;
if (parti)
{
// Make a temporary copy of dedtypes so we don't destroy it
Objects *tmpdedtypes = new Objects();
tmpdedtypes->setDim(dedtypes->length);
memcpy(tmpdedtypes->tdata(), dedtypes->tdata(), dedtypes->length * sizeof(void *));
TypeInstance *t = new TypeInstance(Loc(), parti);
MATCH m = deduceType(t, sc, tparam, parameters, tmpdedtypes);
if (m > MATCHnomatch)
{
// If this is the first ever match, it becomes our best estimate
if (numBaseClassMatches==0)
memcpy(best->tdata(), tmpdedtypes->tdata(), tmpdedtypes->length * sizeof(void *));
else for (size_t k = 0; k < tmpdedtypes->length; ++k)
{
// If we've found more than one possible type for a parameter,
// mark it as unknown.
if ((*tmpdedtypes)[k] != (*best)[k])
(*best)[k] = (*dedtypes)[k];
}
++numBaseClassMatches;
}
}
// Now recursively test the inherited interfaces
for (size_t j = 0; j < b->baseInterfaces.length; ++j)
{
BaseClass *bi = &b->baseInterfaces.ptr[j];
deduceBaseClassParameters(bi,
sc, tparam, parameters, dedtypes,
best, numBaseClassMatches);
}
}
void visit(TypeClass *t)
{
//printf("TypeClass::deduceType(this = %s)\n", t->toChars());
/* If this class is a template class, and we're matching
* it against a template instance, convert the class type
* to a template instance, too, and try again.
*/
TemplateInstance *ti = t->sym->parent->isTemplateInstance();
if (tparam && tparam->ty == Tinstance)
{
if (ti && ti->toAlias() == t->sym)
{
TypeInstance *tx = new TypeInstance(Loc(), ti);
MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
// Even if the match fails, there is still a chance it could match
// a base class.
if (m != MATCHnomatch)
{
result = m;
return;
}
}
/* Match things like:
* S!(T).foo
*/
TypeInstance *tpi = (TypeInstance *)tparam;
if (tpi->idents.length)
{
RootObject *id = tpi->idents[tpi->idents.length - 1];
if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id))
{
Type *tparent = t->sym->parent->getType();
if (tparent)
{
/* Slice off the .foo in S!(T).foo
*/
tpi->idents.length--;
result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
tpi->idents.length++;
return;
}
}
}
// If it matches exactly or via implicit conversion, we're done
visit((Type *)t);
if (result != MATCHnomatch)
return;
/* There is still a chance to match via implicit conversion to
* a base class or interface. Because there could be more than one such
* match, we need to check them all.
*/
int numBaseClassMatches = 0; // Have we found an interface match?
// Our best guess at dedtypes
Objects *best = new Objects();
best->setDim(dedtypes->length);
ClassDeclaration *s = t->sym;
while (s && s->baseclasses->length > 0)
{
// Test the base class
deduceBaseClassParameters((*s->baseclasses)[0],
sc, tparam, parameters, dedtypes,
best, numBaseClassMatches);
// Test the interfaces inherited by the base class
for (size_t i = 0; i < s->interfaces.length; ++i)
{
BaseClass *b = s->interfaces.ptr[i];
deduceBaseClassParameters(b, sc, tparam, parameters, dedtypes,
best, numBaseClassMatches);
}
s = (*s->baseclasses)[0]->sym;
}
if (numBaseClassMatches == 0)
{
result = MATCHnomatch;
return;
}
// If we got at least one match, copy the known types into dedtypes
memcpy(dedtypes->tdata(), best->tdata(), best->length * sizeof(void *));
result = MATCHconvert;
return;
}
// Extra check
if (tparam && tparam->ty == Tclass)
{
TypeClass *tp = (TypeClass *)tparam;
//printf("\t%d\n", (MATCH) t->implicitConvTo(tp));
if (wm && t->deduceWild(tparam, false))
{
result = MATCHconst;
return;
}
result = t->implicitConvTo(tp);
return;
}
visit((Type *)t);
}
void visit(Expression *e)
{
//printf("Expression::deduceType(e = %s)\n", e->toChars());
size_t i = templateParameterLookup(tparam, parameters);
if (i == IDX_NOTFOUND || ((TypeIdentifier *)tparam)->idents.length > 0)
{
if (e == emptyArrayElement && tparam->ty == Tarray)
{
Type *tn = ((TypeNext *)tparam)->next;
result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
return;
}
e->type->accept(this);
return;
}
TemplateTypeParameter *tp = (*parameters)[i]->isTemplateTypeParameter();
if (!tp)
return; // nomatch
if (e == emptyArrayElement)
{
if ((*dedtypes)[i])
{
result = MATCHexact;
return;
}
if (tp->defaultType)
{
tp->defaultType->accept(this);
return;
}
}
Type *at = (Type *)(*dedtypes)[i];
Type *tt;
if (unsigned char wx = deduceWildHelper(e->type, &tt, tparam))
{
*wm |= wx;
result = MATCHconst;
}
else if (MATCH m = deduceTypeHelper(e->type, &tt, tparam))
{
result = m;
}
else
return; // nomatch
// expression vs (none)
if (!at)
{
(*dedtypes)[i] = new TypeDeduced(tt, e, tparam);
return;
}
TypeDeduced *xt = NULL;
if (at->ty == Tnone)
{
xt = (TypeDeduced *)at;
at = xt->tded;
}
// From previous matched expressions to current deduced type
MATCH match1 = xt ? xt->matchAll(tt) : MATCHnomatch;
// From current expresssion to previous deduced type
Type *pt = at->addMod(tparam->mod);
if (*wm)
pt = pt->substWildTo(*wm);
MATCH match2 = e->implicitConvTo(pt);
if (match1 > MATCHnomatch && match2 > MATCHnomatch)
{
if (at->implicitConvTo(tt) <= MATCHnomatch)
match1 = MATCHnomatch; // Prefer at
else if (tt->implicitConvTo(at) <= MATCHnomatch)
match2 = MATCHnomatch; // Prefer tt
else if (tt->isTypeBasic() && tt->ty == at->ty && tt->mod != at->mod)
{
if (!tt->isMutable() && !at->isMutable())
tt = tt->mutableOf()->addMod(MODmerge(tt->mod, at->mod));
else if (tt->isMutable())
{
if (at->mod == 0) // Prefer unshared
match1 = MATCHnomatch;
else
match2 = MATCHnomatch;
}
else if (at->isMutable())
{
if (tt->mod == 0) // Prefer unshared
match2 = MATCHnomatch;
else
match1 = MATCHnomatch;
}
//printf("tt = %s, at = %s\n", tt->toChars(), at->toChars());
}
else
{
match1 = MATCHnomatch;
match2 = MATCHnomatch;
}
}
if (match1 > MATCHnomatch)
{
// Prefer current match: tt
if (xt)
xt->update(tt, e, tparam);
else
(*dedtypes)[i] = tt;
result = match1;
return;
}
if (match2 > MATCHnomatch)
{
// Prefer previous match: (*dedtypes)[i]
if (xt)
xt->update(e, tparam);
result = match2;
return;
}
/* Deduce common type
*/
if (Type *t = rawTypeMerge(at, tt))
{
if (xt)
xt->update(t, e, tparam);
else
(*dedtypes)[i] = t;
pt = tt->addMod(tparam->mod);
if (*wm)
pt = pt->substWildTo(*wm);
result = e->implicitConvTo(pt);
return;
}
result = MATCHnomatch;
}
MATCH deduceEmptyArrayElement()
{
if (!emptyArrayElement)
{
emptyArrayElement = new IdentifierExp(Loc(), Id::p); // dummy
emptyArrayElement->type = Type::tvoid;
}
assert(tparam->ty == Tarray);
Type *tn = ((TypeNext *)tparam)->next;
return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
}
void visit(NullExp *e)
{
if (tparam->ty == Tarray && e->type->ty == Tnull)
{
// tparam:T[] <- e:null (void[])
result = deduceEmptyArrayElement();
return;
}
visit((Expression *)e);
}
void visit(StringExp *e)
{
Type *taai;
if (e->type->ty == Tarray &&
(tparam->ty == Tsarray ||
(tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident &&
((TypeIdentifier *)taai)->idents.length == 0)))
{
// Consider compile-time known boundaries
e->type->nextOf()->sarrayOf(e->len)->accept(this);
return;
}
visit((Expression *)e);
}
void visit(ArrayLiteralExp *e)
{
// https://issues.dlang.org/show_bug.cgi?id=20092
if (e->elements && e->elements->length &&
e->type->toBasetype()->nextOf()->ty == Tvoid)
{
result = deduceEmptyArrayElement();
return;
}
if ((!e->elements || !e->elements->length) &&
e->type->toBasetype()->nextOf()->ty == Tvoid &&
tparam->ty == Tarray)
{
// tparam:T[] <- e:[] (void[])
result = deduceEmptyArrayElement();
return;
}
if (tparam->ty == Tarray && e->elements && e->elements->length)
{
Type *tn = ((TypeDArray *)tparam)->next;
result = MATCHexact;
if (e->basis)
{
MATCH m = deduceType(e->basis, sc, tn, parameters, dedtypes, wm);
if (m < result)
result = m;
}
for (size_t i = 0; i < e->elements->length; i++)
{
if (result <= MATCHnomatch)
break;
Expression *el = (*e->elements)[i];
if (!el)
continue;
MATCH m = deduceType(el, sc, tn, parameters, dedtypes, wm);
if (m < result)
result = m;
}
return;
}
Type *taai;
if (e->type->ty == Tarray &&
(tparam->ty == Tsarray ||
(tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident &&
((TypeIdentifier *)taai)->idents.length == 0)))
{
// Consider compile-time known boundaries
e->type->nextOf()->sarrayOf(e->elements->length)->accept(this);
return;
}
visit((Expression *)e);
}
void visit(AssocArrayLiteralExp *e)
{
if (tparam->ty == Taarray && e->keys && e->keys->length)
{
TypeAArray *taa = (TypeAArray *)tparam;
result = MATCHexact;
for (size_t i = 0; i < e->keys->length; i++)
{
MATCH m1 = deduceType((*e->keys)[i], sc, taa->index, parameters, dedtypes, wm);
if (m1 < result)
result = m1;
if (result <= MATCHnomatch)
break;
MATCH m2 = deduceType((*e->values)[i], sc, taa->next, parameters, dedtypes, wm);
if (m2 < result)
result = m2;
if (result <= MATCHnomatch)
break;
}
return;
}
visit((Expression *)e);
}
void visit(FuncExp *e)
{
//printf("e->type = %s, tparam = %s\n", e->type->toChars(), tparam->toChars());
if (e->td)
{
Type *to = tparam;
if (!to->nextOf() || to->nextOf()->ty != Tfunction)
return;
TypeFunction *tof = (TypeFunction *)to->nextOf();
// Parameter types inference from 'tof'
assert(e->td->_scope);
TypeFunction *tf = (TypeFunction *)e->fd->type;
//printf("\ttof = %s\n", tof->toChars());
//printf("\ttf = %s\n", tf->toChars());
size_t dim = tf->parameterList.length();
if (tof->parameterList.length() != dim ||
tof->parameterList.varargs != tf->parameterList.varargs)
return;
Objects *tiargs = new Objects();
tiargs->reserve(e->td->parameters->length);
for (size_t i = 0; i < e->td->parameters->length; i++)
{
TemplateParameter *tp = (*e->td->parameters)[i];
size_t u = 0;
for (; u < dim; u++)
{
Parameter *p = tf->parameterList[u];
if (p->type->ty == Tident &&
((TypeIdentifier *)p->type)->ident == tp->ident)
{
break;
}
}
assert(u < dim);
Parameter *pto = tof->parameterList[u];
if (!pto)
break;
Type *t = pto->type->syntaxCopy(); // Bugzilla 11774
if (reliesOnTident(t, parameters, inferStart))
return;
t = typeSemantic(t, e->loc, sc);
if (t->ty == Terror)
return;
tiargs->push(t);
}
// Set target of return type inference
if (!tf->next && tof->next)
e->fd->treq = tparam;
TemplateInstance *ti = new TemplateInstance(e->loc, e->td, tiargs);
Expression *ex = new ScopeExp(e->loc, ti);
ex = expressionSemantic(ex, e->td->_scope);
// Reset inference target for the later re-semantic
e->fd->treq = NULL;
if (ex->op == TOKerror)
return;
if (ex->op != TOKfunction)
return;
visit(ex->type);
return;
}
Type *t = e->type;
if (t->ty == Tdelegate && tparam->ty == Tpointer)
return;
// Allow conversion from implicit function pointer to delegate
if (e->tok == TOKreserved &&
t->ty == Tpointer && tparam->ty == Tdelegate)
{
TypeFunction *tf = (TypeFunction *)t->nextOf();
t = (new TypeDelegate(tf))->merge();
}
//printf("tparam = %s <= e->type = %s, t = %s\n", tparam->toChars(), e->type->toChars(), t->toChars());
visit(t);
}
void visit(SliceExp *e)
{
Type *taai;
if (e->type->ty == Tarray &&
(tparam->ty == Tsarray ||
(tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident &&
((TypeIdentifier *)taai)->idents.length == 0)))
{
// Consider compile-time known boundaries
if (Type *tsa = toStaticArrayType(e))
{
tsa->accept(this);
return;
}
}
visit((Expression *)e);
}
void visit(CommaExp *e)
{
((CommaExp *)e)->e2->accept(this);
}
};
DeduceType v(sc, tparam, parameters, dedtypes, wm, inferStart);
if (Type *t = isType(o))
t->accept(&v);
else
{
assert(isExpression(o) && wm);
((Expression *)o)->accept(&v);
}
return v.result;
}
/*******************************
* Input:
* t Tested type, if NULL, returns NULL.
* tparams Optional template parameters.
* == NULL:
* If one of the subtypes of this type is a TypeIdentifier,
* i.e. it's an unresolved type, return that type.
* != NULL:
* Only when the TypeIdentifier is one of template parameters,
* return that type.
*/
bool reliesOnTident(Type *t, TemplateParameters *tparams, size_t iStart)
{
class ReliesOnTident : public Visitor
{
public:
TemplateParameters *tparams;
size_t iStart;
bool result;
ReliesOnTident(TemplateParameters *tparams, size_t iStart)
: tparams(tparams), iStart(iStart)
{
result = false;
}
void visit(Type *)
{
}
void visit(TypeNext *t)
{
t->next->accept(this);
}
void visit(TypeVector *t)
{
t->basetype->accept(this);
}
void visit(TypeAArray *t)
{
visit((TypeNext *)t);
if (!result)
t->index->accept(this);
}
void visit(TypeFunction *t)
{
size_t dim = t->parameterList.length();
for (size_t i = 0; i < dim; i++)
{
Parameter *fparam = t->parameterList[i];
fparam->type->accept(this);
if (result)
return;
}
if (t->next)
t->next->accept(this);
}
void visit(TypeIdentifier *t)
{
if (!tparams)
{
result = true;
return;
}
for (size_t i = iStart; i < tparams->length; i++)
{
TemplateParameter *tp = (*tparams)[i];
if (tp->ident->equals(t->ident))
{
result = true;
return;
}
}
}
void visit(TypeInstance *t)
{
if (!tparams)
return;
for (size_t i = iStart; i < tparams->length; i++)
{
TemplateParameter *tp = (*tparams)[i];
if (t->tempinst->name == tp->ident)
{
result = true;
return;
}
}
if (!t->tempinst->tiargs)
return;
for (size_t i = 0; i < t->tempinst->tiargs->length; i++)
{
Type *ta = isType((*t->tempinst->tiargs)[i]);
if (ta)
{
ta->accept(this);
if (result)
return;
}
}
}
void visit(TypeTypeof *t)
{
//printf("TypeTypeof::reliesOnTident('%s')\n", t->toChars());
t->exp->accept(this);
}
void visit(TypeTuple *t)
{
if (t->arguments)
{
for (size_t i = 0; i < t->arguments->length; i++)
{
Parameter *arg = (*t->arguments)[i];
arg->type->accept(this);
if (result)
return;
}
}
}
void visit(Expression *)
{
//printf("Expression::reliesOnTident('%s')\n", e->toChars());
}
void visit(IdentifierExp *e)
{
//printf("IdentifierExp::reliesOnTident('%s')\n", e->toChars());
for (size_t i = iStart; i < tparams->length; i++)
{
TemplateParameter *tp = (*tparams)[i];
if (e->ident == tp->ident)
{
result = true;
return;
}
}
}
void visit(TupleExp *e)
{
//printf("TupleExp::reliesOnTident('%s')\n", e->toChars());
if (e->exps)
{
for (size_t i = 0; i < e->exps->length; i++)
{
Expression *ea = (*e->exps)[i];
ea->accept(this);
if (result)
return;
}
}
}
void visit(ArrayLiteralExp *e)
{
//printf("ArrayLiteralExp::reliesOnTident('%s')\n", e->toChars());
if (e->elements)
{
for (size_t i = 0; i < e->elements->length; i++)
{
Expression *el = (*e->elements)[i];
el->accept(this);
if (result)
return;
}
}
}
void visit(AssocArrayLiteralExp *e)
{
//printf("AssocArrayLiteralExp::reliesOnTident('%s')\n", e->toChars());
for (size_t i = 0; i < e->keys->length; i++)
{
Expression *ek = (*e->keys)[i];
ek->accept(this);
if (result)
return;
}
for (size_t i = 0; i < e->values->length; i++)
{
Expression *ev = (*e->values)[i];
ev->accept(this);
if (result)
return;
}
}
void visit(StructLiteralExp *e)
{
//printf("StructLiteralExp::reliesOnTident('%s')\n", e->toChars());
if (e->elements)
{
for (size_t i = 0; i < e->elements->length; i++)
{
Expression *ea = (*e->elements)[i];
ea->accept(this);
if (result)
return;
}
}
}
void visit(TypeExp *e)
{
//printf("TypeExp::reliesOnTident('%s')\n", e->toChars());
e->type->accept(this);
}
void visit(NewExp *e)
{
//printf("NewExp::reliesOnTident('%s')\n", e->toChars());
if (e->thisexp)
e->thisexp->accept(this);
if (!result && e->newargs)
{
for (size_t i = 0; i < e->newargs->length; i++)
{
Expression *ea = (*e->newargs)[i];
ea->accept(this);
if (result)
return;
}
}
e->newtype->accept(this);
if (!result && e->arguments)
{
for (size_t i = 0; i < e->arguments->length; i++)
{
Expression *ea = (*e->arguments)[i];
ea->accept(this);
if (result)
return;
}
}
}
void visit(NewAnonClassExp *)
{
//printf("NewAnonClassExp::reliesOnTident('%s')\n", e->toChars());
result = true;
}
void visit(FuncExp *)
{
//printf("FuncExp::reliesOnTident('%s')\n", e->toChars());
result = true;
}
void visit(TypeidExp *e)
{
//printf("TypeidExp::reliesOnTident('%s')\n", e->toChars());
if (Expression *ea = isExpression(e->obj))
ea->accept(this);
else if (Type *ta = isType(e->obj))
ta->accept(this);
}
void visit(TraitsExp *e)
{
//printf("TraitsExp::reliesOnTident('%s')\n", e->toChars());
if (e->args)
{
for (size_t i = 0; i < e->args->length; i++)
{
RootObject *oa = (*e->args)[i];
if (Expression *ea = isExpression(oa))
ea->accept(this);
else if (Type *ta = isType(oa))
ta->accept(this);
if (result)
return;
}
}
}
void visit(IsExp *e)
{
//printf("IsExp::reliesOnTident('%s')\n", e->toChars());
e->targ->accept(this);
}
void visit(UnaExp *e)
{
//printf("UnaExp::reliesOnTident('%s')\n", e->toChars());
e->e1->accept(this);
}
void visit(DotTemplateInstanceExp *e)
{
//printf("DotTemplateInstanceExp::reliesOnTident('%s')\n", e->toChars());
visit((UnaExp *)e);
if (!result && e->ti->tiargs)
{
for (size_t i = 0; i < e->ti->tiargs->length; i++)
{
RootObject *oa = (*e->ti->tiargs)[i];
if (Expression *ea = isExpression(oa))
ea->accept(this);
else if (Type *ta = isType(oa))
ta->accept(this);
if (result)
return;
}
}
}
void visit(CallExp *e)
{
//printf("CallExp::reliesOnTident('%s')\n", e->toChars());
visit((UnaExp *)e);
if (!result && e->arguments)
{
for (size_t i = 0; i < e->arguments->length; i++)
{
Expression *ea = (*e->arguments)[i];
ea->accept(this);
if (result)
return;
}
}
}
void visit(CastExp *e)
{
//printf("CastExp::reliesOnTident('%s')\n", e->toChars());
visit((UnaExp *)e);
// e.to can be null for cast() with no type
if (!result && e->to)
e->to->accept(this);
}
void visit(SliceExp *e)
{
//printf("SliceExp::reliesOnTident('%s')\n", e->toChars());
visit((UnaExp *)e);
if (!result && e->lwr)
e->lwr->accept(this);
if (!result && e->upr)
e->upr->accept(this);
}
void visit(IntervalExp *e)
{
//printf("IntervalExp::reliesOnTident('%s')\n", e->toChars());
e->lwr->accept(this);
if (!result)
e->upr->accept(this);
}
void visit(ArrayExp *e)
{
//printf("ArrayExp::reliesOnTident('%s')\n", e->toChars());
visit((UnaExp *)e);
if (!result && e->arguments)
{
for (size_t i = 0; i < e->arguments->length; i++)
{
Expression *ea = (*e->arguments)[i];
ea->accept(this);
}
}
}
void visit(BinExp *e)
{
//printf("BinExp::reliesOnTident('%s')\n", e->toChars());
e->e1->accept(this);
if (!result)
e->e2->accept(this);
}
void visit(CondExp *e)
{
//printf("BinExp::reliesOnTident('%s')\n", e->toChars());
e->econd->accept(this);
if (!result)
visit((BinExp *)e);
}
};
if (!t)
return false;
ReliesOnTident v(tparams, iStart);
t->accept(&v);
return v.result;
}
/* ======================== TemplateParameter =============================== */
TemplateParameter::TemplateParameter(Loc loc, Identifier *ident)
{
this->loc = loc;
this->ident = ident;
this->dependent = false;
}
TemplateTypeParameter *TemplateParameter::isTemplateTypeParameter()
{
return NULL;
}
TemplateValueParameter *TemplateParameter::isTemplateValueParameter()
{
return NULL;
}
TemplateAliasParameter *TemplateParameter::isTemplateAliasParameter()
{
return NULL;
}
TemplateTupleParameter *TemplateParameter::isTemplateTupleParameter()
{
return NULL;
}
TemplateThisParameter *TemplateParameter::isTemplateThisParameter()
{
return NULL;
}
/*******************************************
* Match to a particular TemplateParameter.
* Input:
* instLoc location that the template is instantiated.
* tiargs[] actual arguments to template instance
* i i'th argument
* parameters[] template parameters
* dedtypes[] deduced arguments to template instance
* *psparam set to symbol declared and initialized to dedtypes[i]
*/
MATCH TemplateParameter::matchArg(Loc instLoc, Scope *sc, Objects *tiargs,
size_t i, TemplateParameters *parameters, Objects *dedtypes,
Declaration **psparam)
{
RootObject *oarg;
if (i < tiargs->length)
oarg = (*tiargs)[i];
else
{
// Get default argument instead
oarg = defaultArg(instLoc, sc);
if (!oarg)
{
assert(i < dedtypes->length);
// It might have already been deduced
oarg = (*dedtypes)[i];
if (!oarg)
goto Lnomatch;
}
}
return matchArg(sc, oarg, i, parameters, dedtypes, psparam);
Lnomatch:
if (psparam)
*psparam = NULL;
return MATCHnomatch;
}
/* ======================== TemplateTypeParameter =========================== */
// type-parameter
Type *TemplateTypeParameter::tdummy = NULL;
TemplateTypeParameter::TemplateTypeParameter(Loc loc, Identifier *ident, Type *specType,
Type *defaultType)
: TemplateParameter(loc, ident)
{
this->ident = ident;
this->specType = specType;
this->defaultType = defaultType;
}
TemplateTypeParameter *TemplateTypeParameter::isTemplateTypeParameter()
{
return this;
}
TemplateParameter *TemplateTypeParameter::syntaxCopy()
{
return new TemplateTypeParameter(loc, ident,
specType ? specType->syntaxCopy() : NULL,
defaultType ? defaultType->syntaxCopy() : NULL);
}
bool TemplateTypeParameter::declareParameter(Scope *sc)
{
//printf("TemplateTypeParameter::declareParameter('%s')\n", ident->toChars());
TypeIdentifier *ti = new TypeIdentifier(loc, ident);
Declaration *ad = new AliasDeclaration(loc, ident, ti);
return sc->insert(ad) != NULL;
}
MATCH TemplateTypeParameter::matchArg(Scope *sc, RootObject *oarg,
size_t i, TemplateParameters *parameters, Objects *dedtypes,
Declaration **psparam)
{
//printf("TemplateTypeParameter::matchArg('%s')\n", ident->toChars());
MATCH m = MATCHexact;
Type *ta = isType(oarg);
if (!ta)
{
//printf("%s %p %p %p\n", oarg->toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg));
goto Lnomatch;
}
//printf("ta is %s\n", ta->toChars());
if (specType)
{
if (!ta || ta == tdummy)
goto Lnomatch;
//printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta->toChars(), specType->toChars());
MATCH m2 = deduceType(ta, sc, specType, parameters, dedtypes);
if (m2 <= MATCHnomatch)
{
//printf("\tfailed deduceType\n");
goto Lnomatch;
}
if (m2 < m)
m = m2;
if ((*dedtypes)[i])
{
Type *t = (Type *)(*dedtypes)[i];
if (dependent && !t->equals(ta)) // Bugzilla 14357
goto Lnomatch;
/* This is a self-dependent parameter. For example:
* template X(T : T*) {}
* template X(T : S!T, alias S) {}
*/
//printf("t = %s ta = %s\n", t->toChars(), ta->toChars());
ta = t;
}
}
else
{
if ((*dedtypes)[i])
{
// Must match already deduced type
Type *t = (Type *)(*dedtypes)[i];
if (!t->equals(ta))
{
//printf("t = %s ta = %s\n", t->toChars(), ta->toChars());
goto Lnomatch;
}
}
else
{
// So that matches with specializations are better
m = MATCHconvert;
}
}
(*dedtypes)[i] = ta;
if (psparam)
*psparam = new AliasDeclaration(loc, ident, ta);
//printf("\tm = %d\n", m);
return dependent ? MATCHexact : m;
Lnomatch:
if (psparam)
*psparam = NULL;
//printf("\tm = %d\n", MATCHnomatch);
return MATCHnomatch;
}
void TemplateTypeParameter::print(RootObject *oarg, RootObject *oded)
{
printf(" %s\n", ident->toChars());
Type *t = isType(oarg);
Type *ta = isType(oded);
assert(ta);
if (specType)
printf("\tSpecialization: %s\n", specType->toChars());
if (defaultType)
printf("\tDefault: %s\n", defaultType->toChars());
printf("\tParameter: %s\n", t ? t->toChars() : "NULL");
printf("\tDeduced Type: %s\n", ta->toChars());
}
void *TemplateTypeParameter::dummyArg()
{
Type *t = specType;
if (!t)
{
// Use this for alias-parameter's too (?)
if (!tdummy)
tdummy = new TypeIdentifier(loc, ident);
t = tdummy;
}
return (void *)t;
}
RootObject *TemplateTypeParameter::specialization()
{
return specType;
}
RootObject *TemplateTypeParameter::defaultArg(Loc, Scope *sc)
{
Type *t = defaultType;
if (t)
{
t = t->syntaxCopy();
t = typeSemantic(t, loc, sc); // use the parameter loc
}
return t;
}
bool TemplateTypeParameter::hasDefaultArg()
{
return defaultType != NULL;
}
/* ======================== TemplateThisParameter =========================== */
// this-parameter
TemplateThisParameter::TemplateThisParameter(Loc loc, Identifier *ident,
Type *specType,
Type *defaultType)
: TemplateTypeParameter(loc, ident, specType, defaultType)
{
}
TemplateThisParameter *TemplateThisParameter::isTemplateThisParameter()
{
return this;
}
TemplateParameter *TemplateThisParameter::syntaxCopy()
{
return new TemplateThisParameter(loc, ident,
specType ? specType->syntaxCopy() : NULL,
defaultType ? defaultType->syntaxCopy() : NULL);
}
/* ======================== TemplateAliasParameter ========================== */
// alias-parameter
Dsymbol *TemplateAliasParameter::sdummy = NULL;
TemplateAliasParameter::TemplateAliasParameter(Loc loc, Identifier *ident,
Type *specType, RootObject *specAlias, RootObject *defaultAlias)
: TemplateParameter(loc, ident)
{
this->ident = ident;
this->specType = specType;
this->specAlias = specAlias;
this->defaultAlias = defaultAlias;
}
TemplateAliasParameter *TemplateAliasParameter::isTemplateAliasParameter()
{
return this;
}
TemplateParameter *TemplateAliasParameter::syntaxCopy()
{
return new TemplateAliasParameter(loc, ident,
specType ? specType->syntaxCopy() : NULL,
objectSyntaxCopy(specAlias),
objectSyntaxCopy(defaultAlias));
}
bool TemplateAliasParameter::declareParameter(Scope *sc)
{
TypeIdentifier *ti = new TypeIdentifier(loc, ident);
Declaration *ad = new AliasDeclaration(loc, ident, ti);
return sc->insert(ad) != NULL;
}
MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg,
size_t i, TemplateParameters *parameters, Objects *dedtypes,
Declaration **psparam)
{
//printf("TemplateAliasParameter::matchArg('%s')\n", ident->toChars());
MATCH m = MATCHexact;
Type *ta = isType(oarg);
RootObject *sa = ta && !ta->deco ? NULL : getDsymbol(oarg);
Expression *ea = isExpression(oarg);
if (ea && (ea->op == TOKthis || ea->op == TOKsuper))
sa = ((ThisExp *)ea)->var;
else if (ea && ea->op == TOKscope)
sa = ((ScopeExp *)ea)->sds;
if (sa)
{
if (((Dsymbol *)sa)->isAggregateDeclaration())
m = MATCHconvert;
/* specType means the alias must be a declaration with a type
* that matches specType.
*/
if (specType)
{
Declaration *d = ((Dsymbol *)sa)->isDeclaration();
if (!d)
goto Lnomatch;
if (!d->type->equals(specType))
goto Lnomatch;
}
}
else
{
sa = oarg;
if (ea)
{
if (specType)
{
if (!ea->type->equals(specType))
goto Lnomatch;
}
}
else if (ta && ta->ty == Tinstance && !specAlias)
{
/* Bugzilla xxxxx: Specialized parameter should be prefeerd
* match to the template type parameter.
* template X(alias a) {} // a == this
* template X(alias a : B!A, alias B, A...) {} // B!A => ta
*/
}
else if (sa && sa == TemplateTypeParameter::tdummy)
{
/* Bugzilla 2025: Aggregate Types should preferentially
* match to the template type parameter.
* template X(alias a) {} // a == this
* template X(T) {} // T => sa
*/
}
else if (ta && ta->ty != Tident)
{
/* Match any type that's not a TypeIdentifier to alias parameters,
* but prefer type parameter.
* template X(alias a) { } // a == ta
*
* TypeIdentifiers are excluded because they might be not yet resolved aliases.
*/
m = MATCHconvert;
}
else
goto Lnomatch;
}
if (specAlias)
{
if (sa == sdummy)
goto Lnomatch;
Dsymbol *sx = isDsymbol(sa);
if (sa != specAlias && sx)
{
Type *talias = isType(specAlias);
if (!talias)
goto Lnomatch;
TemplateInstance *ti = sx->isTemplateInstance();
if (!ti && sx->parent)
{
ti = sx->parent->isTemplateInstance();
if (ti && ti->name != sx->ident)
goto Lnomatch;
}
if (!ti)
goto Lnomatch;
Type *t = new TypeInstance(Loc(), ti);
MATCH m2 = deduceType(t, sc, talias, parameters, dedtypes);
if (m2 <= MATCHnomatch)
goto Lnomatch;
}
}
else if ((*dedtypes)[i])
{
// Must match already deduced symbol
RootObject *si = (*dedtypes)[i];
if (!sa || si != sa)
goto Lnomatch;
}
(*dedtypes)[i] = sa;
if (psparam)
{
if (Dsymbol *s = isDsymbol(sa))
{
*psparam = new AliasDeclaration(loc, ident, s);
}
else if (Type *t = isType(sa))
{
*psparam = new AliasDeclaration(loc, ident, t);
}
else
{
assert(ea);
// Declare manifest constant
Initializer *init = new ExpInitializer(loc, ea);
VarDeclaration *v = new VarDeclaration(loc, NULL, ident, init);
v->storage_class = STCmanifest;
dsymbolSemantic(v, sc);
*psparam = v;
}
}
return dependent ? MATCHexact : m;
Lnomatch:
if (psparam)
*psparam = NULL;
//printf("\tm = %d\n", MATCHnomatch);
return MATCHnomatch;
}
void TemplateAliasParameter::print(RootObject *, RootObject *oded)
{
printf(" %s\n", ident->toChars());
Dsymbol *sa = isDsymbol(oded);
assert(sa);
printf("\tParameter alias: %s\n", sa->toChars());
}
void *TemplateAliasParameter::dummyArg()
{
RootObject *s = specAlias;
if (!s)
{
if (!sdummy)
sdummy = new Dsymbol();
s = sdummy;
}
return (void*)s;
}
RootObject *TemplateAliasParameter::specialization()
{
return specAlias;
}
RootObject *TemplateAliasParameter::defaultArg(Loc, Scope *sc)
{
RootObject *da = defaultAlias;
Type *ta = isType(defaultAlias);
if (ta)
{
if (ta->ty == Tinstance)
{
// If the default arg is a template, instantiate for each type
da = ta->syntaxCopy();
}
}
RootObject *o = aliasParameterSemantic(loc, sc, da, NULL); // use the parameter loc
return o;
}
bool TemplateAliasParameter::hasDefaultArg()
{
return defaultAlias != NULL;
}
/* ======================== TemplateValueParameter ========================== */
// value-parameter
AA *TemplateValueParameter::edummies = NULL;
TemplateValueParameter::TemplateValueParameter(Loc loc, Identifier *ident, Type *valType,
Expression *specValue, Expression *defaultValue)
: TemplateParameter(loc, ident)
{
this->ident = ident;
this->valType = valType;
this->specValue = specValue;
this->defaultValue = defaultValue;
}
TemplateValueParameter *TemplateValueParameter::isTemplateValueParameter()
{
return this;
}
TemplateParameter *TemplateValueParameter::syntaxCopy()
{
return new TemplateValueParameter(loc, ident,
valType->syntaxCopy(),
specValue ? specValue->syntaxCopy() : NULL,
defaultValue ? defaultValue->syntaxCopy() : NULL);
}
bool TemplateValueParameter::declareParameter(Scope *sc)
{
VarDeclaration *v = new VarDeclaration(loc, valType, ident, NULL);
v->storage_class = STCtemplateparameter;
return sc->insert(v) != NULL;
}
MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg,
size_t i, TemplateParameters *, Objects *dedtypes, Declaration **psparam)
{
//printf("TemplateValueParameter::matchArg('%s')\n", ident->toChars());
MATCH m = MATCHexact;
Expression *ei = isExpression(oarg);
Type *vt;
if (!ei && oarg)
{
Dsymbol *si = isDsymbol(oarg);
FuncDeclaration *f = si ? si->isFuncDeclaration() : NULL;
if (!f || !f->fbody || f->needThis())
goto Lnomatch;
ei = new VarExp(loc, f);
ei = expressionSemantic(ei, sc);
/* If a function is really property-like, and then
* it's CTFEable, ei will be a literal expression.
*/
unsigned int olderrors = global.startGagging();
ei = resolveProperties(sc, ei);
ei = ei->ctfeInterpret();
if (global.endGagging(olderrors) || ei->op == TOKerror)
goto Lnomatch;
/* Bugzilla 14520: A property-like function can match to both
* TemplateAlias and ValueParameter. But for template overloads,
* it should always prefer alias parameter to be consistent
* template match result.
*
* template X(alias f) { enum X = 1; }
* template X(int val) { enum X = 2; }
* int f1() { return 0; } // CTFEable
* int f2(); // body-less function is not CTFEable
* enum x1 = X!f1; // should be 1
* enum x2 = X!f2; // should be 1
*
* e.g. The x1 value must be same even if the f1 definition will be moved
* into di while stripping body code.
*/
m = MATCHconvert;
}
if (ei && ei->op == TOKvar)
{
// Resolve const variables that we had skipped earlier
ei = ei->ctfeInterpret();
}
//printf("\tvalType: %s, ty = %d\n", valType->toChars(), valType->ty);
vt = typeSemantic(valType, loc, sc);
//printf("ei: %s, ei->type: %s\n", ei->toChars(), ei->type->toChars());
//printf("vt = %s\n", vt->toChars());
if (ei->type)
{
MATCH m2 = ei->implicitConvTo(vt);
//printf("m: %d\n", m);
if (m2 < m)
m = m2;
if (m <= MATCHnomatch)
goto Lnomatch;
ei = ei->implicitCastTo(sc, vt);
ei = ei->ctfeInterpret();
}
if (specValue)
{
if (!ei || (Expression *)dmd_aaGetRvalue(edummies, (void *)ei->type) == ei)
goto Lnomatch;
Expression *e = specValue;
sc = sc->startCTFE();
e = expressionSemantic(e, sc);
e = resolveProperties(sc, e);
sc = sc->endCTFE();
e = e->implicitCastTo(sc, vt);
e = e->ctfeInterpret();
ei = ei->syntaxCopy();
sc = sc->startCTFE();
ei = expressionSemantic(ei, sc);
sc = sc->endCTFE();
ei = ei->implicitCastTo(sc, vt);
ei = ei->ctfeInterpret();
//printf("\tei: %s, %s\n", ei->toChars(), ei->type->toChars());
//printf("\te : %s, %s\n", e->toChars(), e->type->toChars());
if (!ei->equals(e))
goto Lnomatch;
}
else
{
if ((*dedtypes)[i])
{
// Must match already deduced value
Expression *e = (Expression *)(*dedtypes)[i];
if (!ei || !ei->equals(e))
goto Lnomatch;
}
}
(*dedtypes)[i] = ei;
if (psparam)
{
Initializer *init = new ExpInitializer(loc, ei);
Declaration *sparam = new VarDeclaration(loc, vt, ident, init);
sparam->storage_class = STCmanifest;
*psparam = sparam;
}
return dependent ? MATCHexact : m;
Lnomatch:
//printf("\tno match\n");
if (psparam)
*psparam = NULL;
return MATCHnomatch;
}
void TemplateValueParameter::print(RootObject *, RootObject *oded)
{
printf(" %s\n", ident->toChars());
Expression *ea = isExpression(oded);
if (specValue)
printf("\tSpecialization: %s\n", specValue->toChars());
printf("\tParameter Value: %s\n", ea ? ea->toChars() : "NULL");
}
void *TemplateValueParameter::dummyArg()
{
Expression *e = specValue;
if (!e)
{
// Create a dummy value
Expression **pe = (Expression **)dmd_aaGet(&edummies, (void *)valType);
if (!*pe)
*pe = valType->defaultInit();
e = *pe;
}
return (void *)e;
}
RootObject *TemplateValueParameter::specialization()
{
return specValue;
}
RootObject *TemplateValueParameter::defaultArg(Loc instLoc, Scope *sc)
{
Expression *e = defaultValue;
if (e)
{
e = e->syntaxCopy();
unsigned olderrs = global.errors;
if ((e = expressionSemantic(e, sc)) == NULL)
return NULL;
if ((e = resolveProperties(sc, e)) == NULL)
return NULL;
e = e->resolveLoc(instLoc, sc); // use the instantiated loc
e = e->optimize(WANTvalue);
if (global.errors != olderrs)
e = new ErrorExp();
}
return e;
}
bool TemplateValueParameter::hasDefaultArg()
{
return defaultValue != NULL;
}
/* ======================== TemplateTupleParameter ========================== */
// variadic-parameter
TemplateTupleParameter::TemplateTupleParameter(Loc loc, Identifier *ident)
: TemplateParameter(loc, ident)
{
this->ident = ident;
}
TemplateTupleParameter *TemplateTupleParameter::isTemplateTupleParameter()
{
return this;
}
TemplateParameter *TemplateTupleParameter::syntaxCopy()
{
return new TemplateTupleParameter(loc, ident);
}
bool TemplateTupleParameter::declareParameter(Scope *sc)
{
TypeIdentifier *ti = new TypeIdentifier(loc, ident);
Declaration *ad = new AliasDeclaration(loc, ident, ti);
return sc->insert(ad) != NULL;
}
MATCH TemplateTupleParameter::matchArg(Loc, Scope *sc, Objects *tiargs,
size_t i, TemplateParameters *parameters, Objects *dedtypes,
Declaration **psparam)
{
/* The rest of the actual arguments (tiargs[]) form the match
* for the variadic parameter.
*/
assert(i + 1 == dedtypes->length); // must be the last one
Tuple *ovar;
if (Tuple *u = isTuple((*dedtypes)[i]))
{
// It has already been deduced
ovar = u;
}
else if (i + 1 == tiargs->length && isTuple((*tiargs)[i]))
ovar = isTuple((*tiargs)[i]);
else
{
ovar = new Tuple();
//printf("ovar = %p\n", ovar);
if (i < tiargs->length)
{
//printf("i = %d, tiargs->length = %d\n", i, tiargs->length);
ovar->objects.setDim(tiargs->length - i);
for (size_t j = 0; j < ovar->objects.length; j++)
ovar->objects[j] = (*tiargs)[i + j];
}
}
return matchArg(sc, ovar, i, parameters, dedtypes, psparam);
}
MATCH TemplateTupleParameter::matchArg(Scope *, RootObject *oarg,
size_t i, TemplateParameters *, Objects *dedtypes, Declaration **psparam)
{
//printf("TemplateTupleParameter::matchArg('%s')\n", ident->toChars());
Tuple *ovar = isTuple(oarg);
if (!ovar)
return MATCHnomatch;
if ((*dedtypes)[i])
{
Tuple *tup = isTuple((*dedtypes)[i]);
if (!tup)
return MATCHnomatch;
if (!match(tup, ovar))
return MATCHnomatch;
}
(*dedtypes)[i] = ovar;
if (psparam)
*psparam = new TupleDeclaration(loc, ident, &ovar->objects);
return dependent ? MATCHexact : MATCHconvert;
}
void TemplateTupleParameter::print(RootObject *, RootObject *oded)
{
printf(" %s... [", ident->toChars());
Tuple *v = isTuple(oded);
assert(v);
//printf("|%d| ", v->objects.length);
for (size_t i = 0; i < v->objects.length; i++)
{
if (i)
printf(", ");
RootObject *o = v->objects[i];
Dsymbol *sa = isDsymbol(o);
if (sa)
printf("alias: %s", sa->toChars());
Type *ta = isType(o);
if (ta)
printf("type: %s", ta->toChars());
Expression *ea = isExpression(o);
if (ea)
printf("exp: %s", ea->toChars());
assert(!isTuple(o)); // no nested Tuple arguments
}
printf("]\n");
}
void *TemplateTupleParameter::dummyArg()
{
return NULL;
}
RootObject *TemplateTupleParameter::specialization()
{
return NULL;
}
RootObject *TemplateTupleParameter::defaultArg(Loc, Scope *)
{
return NULL;
}
bool TemplateTupleParameter::hasDefaultArg()
{
return false;
}
/* ======================== TemplateInstance ================================ */
TemplateInstance::TemplateInstance(Loc loc, Identifier *ident)
: ScopeDsymbol(NULL)
{
this->loc = loc;
this->name = ident;
this->tiargs = NULL;
this->tempdecl = NULL;
this->inst = NULL;
this->tinst = NULL;
this->tnext = NULL;
this->minst = NULL;
this->deferred = NULL;
this->memberOf = NULL;
this->argsym = NULL;
this->aliasdecl = NULL;
this->semantictiargsdone = false;
this->inuse = 0;
this->nest = 0;
this->havetempdecl = false;
this->enclosing = NULL;
this->gagged = false;
this->hash = 0;
this->fargs = NULL;
}
/*****************
* This constructor is only called when we figured out which function
* template to instantiate.
*/
TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *tiargs)
: ScopeDsymbol(NULL)
{
this->loc = loc;
this->name = td->ident;
this->tiargs = tiargs;
this->tempdecl = td;
this->inst = NULL;
this->tinst = NULL;
this->tnext = NULL;
this->minst = NULL;
this->deferred = NULL;
this->memberOf = NULL;
this->argsym = NULL;
this->aliasdecl = NULL;
this->semantictiargsdone = true;
this->inuse = 0;
this->nest = 0;
this->havetempdecl = true;
this->enclosing = NULL;
this->gagged = false;
this->hash = 0;
this->fargs = NULL;
assert(tempdecl->_scope);
}
Objects *TemplateInstance::arraySyntaxCopy(Objects *objs)
{
Objects *a = NULL;
if (objs)
{
a = new Objects();
a->setDim(objs->length);
for (size_t i = 0; i < objs->length; i++)
(*a)[i] = objectSyntaxCopy((*objs)[i]);
}
return a;
}
Dsymbol *TemplateInstance::syntaxCopy(Dsymbol *s)
{
TemplateInstance *ti =
s ? (TemplateInstance *)s
: new TemplateInstance(loc, name);
ti->tiargs = arraySyntaxCopy(tiargs);
TemplateDeclaration *td;
if (inst && tempdecl && (td = tempdecl->isTemplateDeclaration()) != NULL)
td->ScopeDsymbol::syntaxCopy(ti);
else
ScopeDsymbol::syntaxCopy(ti);
return ti;
}
void TemplateInstance::expandMembers(Scope *sc2)
{
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
s->setScope(sc2);
}
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
s->importAll(sc2);
}
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
//printf("\t[%d] semantic on '%s' %p kind %s in '%s'\n", i, s->toChars(), s, s->kind(), this->toChars());
//printf("test: enclosing = %d, sc2->parent = %s\n", enclosing, sc2->parent->toChars());
// if (enclosing)
// s->parent = sc->parent;
//printf("test3: enclosing = %d, s->parent = %s\n", enclosing, s->parent->toChars());
dsymbolSemantic(s, sc2);
//printf("test4: enclosing = %d, s->parent = %s\n", enclosing, s->parent->toChars());
Module::runDeferredSemantic();
}
}
void TemplateInstance::tryExpandMembers(Scope *sc2)
{
static int nest;
// extracted to a function to allow windows SEH to work without destructors in the same function
//printf("%d\n", nest);
if (++nest > global.recursionLimit)
{
global.gag = 0; // ensure error message gets printed
error("recursive expansion exceeded allowed nesting limit");
fatal();
}
expandMembers(sc2);
nest--;
}
void TemplateInstance::trySemantic3(Scope *sc2)
{
// extracted to a function to allow windows SEH to work without destructors in the same function
static int nest;
//printf("%d\n", nest);
if (++nest > global.recursionLimit)
{
global.gag = 0; // ensure error message gets printed
error("recursive expansion exceeded allowed nesting limit");
fatal();
}
semantic3(this, sc2);
--nest;
}
/**********************************************
* Find template declaration corresponding to template instance.
*
* Returns:
* false if finding fails.
* Note:
* This function is reentrant against error occurrence. If returns false,
* any members of this object won't be modified, and repetition call will
* reproduce same error.
*/
bool TemplateInstance::findTempDecl(Scope *sc, WithScopeSymbol **pwithsym)
{
if (pwithsym)
*pwithsym = NULL;
if (havetempdecl)
return true;
//printf("TemplateInstance::findTempDecl() %s\n", toChars());
if (!tempdecl)
{
/* Given:
* foo!( ... )
* figure out which TemplateDeclaration foo refers to.
*/
Identifier *id = name;
Dsymbol *scopesym;
Dsymbol *s = sc->search(loc, id, &scopesym);
if (!s)
{
s = sc->search_correct(id);
if (s)
error("template `%s` is not defined, did you mean %s?", id->toChars(), s->toChars());
else
error("template `%s` is not defined", id->toChars());
return false;
}
if (pwithsym)
*pwithsym = scopesym->isWithScopeSymbol();
/* We might have found an alias within a template when
* we really want the template.
*/
TemplateInstance *ti;
if (s->parent &&
(ti = s->parent->isTemplateInstance()) != NULL)
{
if (ti->tempdecl && ti->tempdecl->ident == id)
{
/* This is so that one can refer to the enclosing
* template, even if it has the same name as a member
* of the template, if it has a !(arguments)
*/
TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration();
assert(td);
if (td->overroot) // if not start of overloaded list of TemplateDeclaration's
td = td->overroot; // then get the start
s = td;
}
}
if (!updateTempDecl(sc, s))
{
return false;
}
}
assert(tempdecl);
struct ParamFwdTi
{
static int fp(void *param, Dsymbol *s)
{
TemplateDeclaration *td = s->isTemplateDeclaration();
if (!td)
return 0;
TemplateInstance *ti = (TemplateInstance *)param;
if (td->semanticRun == PASSinit)
{
if (td->_scope)
{
// Try to fix forward reference. Ungag errors while doing so.
Ungag ungag = td->ungagSpeculative();
dsymbolSemantic(td, td->_scope);
}
if (td->semanticRun == PASSinit)
{
ti->error("%s forward references template declaration %s", ti->toChars(), td->toChars());
return 1;
}
}
return 0;
}
};
// Look for forward references
OverloadSet *tovers = tempdecl->isOverloadSet();
size_t overs_dim = tovers ? tovers->a.length : 1;
for (size_t oi = 0; oi < overs_dim; oi++)
{
if (overloadApply(tovers ? tovers->a[oi] : tempdecl, (void *)this, &ParamFwdTi::fp))
return false;
}
return true;
}
/**********************************************
* Confirm s is a valid template, then store it.
* Input:
* sc
* s candidate symbol of template. It may be:
* TemplateDeclaration
* FuncDeclaration with findTemplateDeclRoot() != NULL
* OverloadSet which contains candidates
* Returns:
* true if updating succeeds.
*/
bool TemplateInstance::updateTempDecl(Scope *sc, Dsymbol *s)
{
if (s)
{
Identifier *id = name;
s = s->toAlias();
/* If an OverloadSet, look for a unique member that is a template declaration
*/
OverloadSet *os = s->isOverloadSet();
if (os)
{
s = NULL;
for (size_t i = 0; i < os->a.length; i++)
{
Dsymbol *s2 = os->a[i];
if (FuncDeclaration *f = s2->isFuncDeclaration())
s2 = f->findTemplateDeclRoot();
else
s2 = s2->isTemplateDeclaration();
if (s2)
{
if (s)
{
tempdecl = os;
return true;
}
s = s2;
}
}
if (!s)
{
error("template `%s` is not defined", id->toChars());
return false;
}
}
OverDeclaration *od = s->isOverDeclaration();
if (od)
{
tempdecl = od; // TODO: more strict check
return true;
}
/* It should be a TemplateDeclaration, not some other symbol
*/
if (FuncDeclaration *f = s->isFuncDeclaration())
tempdecl = f->findTemplateDeclRoot();
else
tempdecl = s->isTemplateDeclaration();
if (!tempdecl)
{
if (!s->parent && global.errors)
return false;
if (!s->parent && s->getType())
{
Dsymbol *s2 = s->getType()->toDsymbol(sc);
if (!s2)
{
error("%s is not a template declaration, it is a %s", id->toChars(), s->kind());
return false;
}
s = s2;
}
//assert(s->parent);
TemplateInstance *ti = s->parent ? s->parent->isTemplateInstance() : NULL;
if (ti &&
(ti->name == s->ident ||
ti->toAlias()->ident == s->ident)
&&
ti->tempdecl)
{
/* This is so that one can refer to the enclosing
* template, even if it has the same name as a member
* of the template, if it has a !(arguments)
*/
TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration();
assert(td);
if (td->overroot) // if not start of overloaded list of TemplateDeclaration's
td = td->overroot; // then get the start
tempdecl = td;
}
else
{
error("%s is not a template declaration, it is a %s", id->toChars(), s->kind());
return false;
}
}
}
return (tempdecl != NULL);
}
/**********************************
* Run semantic on the elements of tiargs.
* Input:
* sc
* Returns:
* false if one or more arguments have errors.
* Note:
* This function is reentrant against error occurrence. If returns false,
* all elements of tiargs won't be modified.
*/
bool TemplateInstance::semanticTiargs(Scope *sc)
{
//printf("+TemplateInstance::semanticTiargs() %s\n", toChars());
if (semantictiargsdone)
return true;
if (semanticTiargs(loc, sc, tiargs, 0))
{
// cache the result iff semantic analysis succeeded entirely
semantictiargsdone = 1;
return true;
}
return false;
}
/**********************************
* Run semantic of tiargs as arguments of template.
* Input:
* loc
* sc
* tiargs array of template arguments
* flags 1: replace const variables with their initializers
* 2: don't devolve Parameter to Type
* Returns:
* false if one or more arguments have errors.
*/
bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags)
{
// Run semantic on each argument, place results in tiargs[]
//printf("+TemplateInstance::semanticTiargs()\n");
if (!tiargs)
return true;
bool err = false;
for (size_t j = 0; j < tiargs->length; j++)
{
RootObject *o = (*tiargs)[j];
Type *ta = isType(o);
Expression *ea = isExpression(o);
Dsymbol *sa = isDsymbol(o);
//printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta);
if (ta)
{
//printf("type %s\n", ta->toChars());
// It might really be an Expression or an Alias
ta->resolve(loc, sc, &ea, &ta, &sa, (flags & 1) != 0);
if (ea) goto Lexpr;
if (sa) goto Ldsym;
if (ta == NULL)
{
assert(global.errors);
ta = Type::terror;
}
Ltype:
if (ta->ty == Ttuple)
{
// Expand tuple
TypeTuple *tt = (TypeTuple *)ta;
size_t dim = tt->arguments->length;
tiargs->remove(j);
if (dim)
{
tiargs->reserve(dim);
for (size_t i = 0; i < dim; i++)
{
Parameter *arg = (*tt->arguments)[i];
if (flags & 2 && (arg->ident || arg->userAttribDecl))
tiargs->insert(j + i, arg);
else
tiargs->insert(j + i, arg->type);
}
}
j--;
continue;
}
if (ta->ty == Terror)
{
err = true;
continue;
}
(*tiargs)[j] = ta->merge2();
}
else if (ea)
{
Lexpr:
//printf("+[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars());
if (flags & 1) // only used by __traits
{
ea = expressionSemantic(ea, sc);
// must not interpret the args, excepting template parameters
if (ea->op != TOKvar ||
(((VarExp *)ea)->var->storage_class & STCtemplateparameter))
{
ea = ea->optimize(WANTvalue);
}
}
else
{
sc = sc->startCTFE();
ea = expressionSemantic(ea, sc);
sc = sc->endCTFE();
if (ea->op == TOKvar)
{
/* This test is to skip substituting a const var with
* its initializer. The problem is the initializer won't
* match with an 'alias' parameter. Instead, do the
* const substitution in TemplateValueParameter::matchArg().
*/
}
else if (definitelyValueParameter(ea))
{
if (ea->checkValue()) // check void expression
ea = new ErrorExp();
unsigned int olderrs = global.errors;
ea = ea->ctfeInterpret();
if (global.errors != olderrs)
ea = new ErrorExp();
}
}
//printf("-[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars());
if (ea->op == TOKtuple)
{
// Expand tuple
TupleExp *te = (TupleExp *)ea;
size_t dim = te->exps->length;
tiargs->remove(j);
if (dim)
{
tiargs->reserve(dim);
for (size_t i = 0; i < dim; i++)
tiargs->insert(j + i, (*te->exps)[i]);
}
j--;
continue;
}
if (ea->op == TOKerror)
{
err = true;
continue;
}
(*tiargs)[j] = ea;
if (ea->op == TOKtype)
{
ta = ea->type;
goto Ltype;
}
if (ea->op == TOKscope)
{
sa = ((ScopeExp *)ea)->sds;
goto Ldsym;
}
if (ea->op == TOKfunction)
{
FuncExp *fe = (FuncExp *)ea;
/* A function literal, that is passed to template and
* already semanticed as function pointer, never requires
* outer frame. So convert it to global function is valid.
*/
if (fe->fd->tok == TOKreserved && fe->type->ty == Tpointer)
{
// change to non-nested
fe->fd->tok = TOKfunction;
fe->fd->vthis = NULL;
}
else if (fe->td)
{
/* If template argument is a template lambda,
* get template declaration itself. */
//sa = fe->td;
//goto Ldsym;
}
}
if (ea->op == TOKdotvar && !(flags & 1))
{
// translate expression to dsymbol.
sa = ((DotVarExp *)ea)->var;
goto Ldsym;
}
if (ea->op == TOKtemplate)
{
sa = ((TemplateExp *)ea)->td;
goto Ldsym;
}
if (ea->op == TOKdottd && !(flags & 1))
{
// translate expression to dsymbol.
sa = ((DotTemplateExp *)ea)->td;
goto Ldsym;
}
if (ea->op == TOKdot)
{
if (ScopeExp *se = ((DotExp *)ea)->e2->isScopeExp())
{
sa = se->sds;
goto Ldsym;
}
}
}
else if (sa)
{
Ldsym:
//printf("dsym %s %s\n", sa->kind(), sa->toChars());
if (sa->errors)
{
err = true;
continue;
}
TupleDeclaration *d = sa->toAlias()->isTupleDeclaration();
if (d)
{
// Expand tuple
tiargs->remove(j);
tiargs->insert(j, d->objects);
j--;
continue;
}
if (FuncAliasDeclaration *fa = sa->isFuncAliasDeclaration())
{
FuncDeclaration *f = fa->toAliasFunc();
if (!fa->hasOverloads && f->isUnique())
{
// Strip FuncAlias only when the aliased function
// does not have any overloads.
sa = f;
}
}
(*tiargs)[j] = sa;
TemplateDeclaration *td = sa->isTemplateDeclaration();
if (td && td->semanticRun == PASSinit && td->literal)
{
dsymbolSemantic(td, sc);
}
FuncDeclaration *fd = sa->isFuncDeclaration();
if (fd)
fd->functionSemantic();
}
else if (isParameter(o))
{
}
else
{
assert(0);
}
//printf("1: (*tiargs)[%d] = %p\n", j, (*tiargs)[j]);
}
return !err;
}
bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs)
{
if (havetempdecl)
{
TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration();
assert(tempdecl);
assert(tempdecl->_scope);
// Deduce tdtypes
tdtypes.setDim(tempdecl->parameters->length);
if (!tempdecl->matchWithInstance(sc, this, &tdtypes, fargs, 2))
{
error("incompatible arguments for template instantiation");
return false;
}
// TODO: Normalizing tiargs for bugzilla 7469 is necessary?
return true;
}
unsigned errs = global.errors;
TemplateDeclaration *td_last = NULL;
struct ParamBest
{
// context
Scope *sc;
TemplateInstance *ti;
Objects dedtypes;
// result
TemplateDeclaration *td_best;
TemplateDeclaration *td_ambig;
MATCH m_best;
static int fp(void *param, Dsymbol *s)
{
return ((ParamBest *)param)->fp(s);
}
int fp(Dsymbol *s)
{
TemplateDeclaration *td = s->isTemplateDeclaration();
if (!td)
return 0;
if (td->inuse)
{
td->error(ti->loc, "recursive template expansion");
return 1;
}
if (td == td_best) // skip duplicates
return 0;
//printf("td = %s\n", td->toPrettyChars());
// If more arguments than parameters,
// then this is no match.
if (td->parameters->length < ti->tiargs->length)
{
if (!td->isVariadic())
return 0;
}
dedtypes.setDim(td->parameters->length);
dedtypes.zero();
assert(td->semanticRun != PASSinit);
MATCH m = td->matchWithInstance(sc, ti, &dedtypes, ti->fargs, 0);
//printf("matchWithInstance = %d\n", m);
if (m <= MATCHnomatch) // no match at all
return 0;
if (m < m_best) goto Ltd_best;
if (m > m_best) goto Ltd;
{
// Disambiguate by picking the most specialized TemplateDeclaration
MATCH c1 = td->leastAsSpecialized(sc, td_best, ti->fargs);
MATCH c2 = td_best->leastAsSpecialized(sc, td, ti->fargs);
//printf("c1 = %d, c2 = %d\n", c1, c2);
if (c1 > c2) goto Ltd;
if (c1 < c2) goto Ltd_best;
}
td_ambig = td;
return 0;
Ltd_best: // td_best is the best match so far
td_ambig = NULL;
return 0;
Ltd: // td is the new best match
td_ambig = NULL;
td_best = td;
m_best = m;
ti->tdtypes.setDim(dedtypes.length);
memcpy(ti->tdtypes.tdata(), dedtypes.tdata(), ti->tdtypes.length * sizeof(void *));
return 0;
}
};
ParamBest p;
// context
p.ti = this;
p.sc = sc;
/* Since there can be multiple TemplateDeclaration's with the same
* name, look for the best match.
*/
OverloadSet *tovers = tempdecl->isOverloadSet();
size_t overs_dim = tovers ? tovers->a.length : 1;
for (size_t oi = 0; oi < overs_dim; oi++)
{
// result
p.td_best = NULL;
p.td_ambig = NULL;
p.m_best = MATCHnomatch;
Dsymbol *dstart = tovers ? tovers->a[oi] : tempdecl;
overloadApply(dstart, &p, &ParamBest::fp);
if (p.td_ambig)
{
::error(loc, "%s %s.%s matches more than one template declaration:\n%s: %s\nand\n%s: %s",
p.td_best->kind(), p.td_best->parent->toPrettyChars(), p.td_best->ident->toChars(),
p.td_best->loc.toChars() , p.td_best->toChars(),
p.td_ambig->loc.toChars(), p.td_ambig->toChars());
return false;
}
if (p.td_best)
{
if (!td_last)
td_last = p.td_best;
else if (td_last != p.td_best)
{
ScopeDsymbol::multiplyDefined(loc, td_last, p.td_best);
return false;
}
}
}
if (td_last)
{
/* Bugzilla 7469: Normalize tiargs by using corresponding deduced
* template value parameters and tuples for the correct mangling.
*
* By doing this before hasNestedArgs, CTFEable local variable will be
* accepted as a value parameter. For example:
*
* void foo() {
* struct S(int n) {} // non-global template
* const int num = 1; // CTFEable local variable
* S!num s; // S!1 is instantiated, not S!num
* }
*/
size_t dim = td_last->parameters->length - (td_last->isVariadic() ? 1 : 0);
for (size_t i = 0; i < dim; i++)
{
if (tiargs->length <= i)
tiargs->push(tdtypes[i]);
assert(i < tiargs->length);
TemplateValueParameter *tvp = (*td_last->parameters)[i]->isTemplateValueParameter();
if (!tvp)
continue;
assert(tdtypes[i]);
// tdtypes[i] is already normalized to the required type in matchArg
(*tiargs)[i] = tdtypes[i];
}
if (td_last->isVariadic() && tiargs->length == dim && tdtypes[dim])
{
Tuple *va = isTuple(tdtypes[dim]);
assert(va);
for (size_t i = 0; i < va->objects.length; i++)
tiargs->push(va->objects[i]);
}
}
else if (errors && inst)
{
// instantiation was failed with error reporting
assert(global.errors);
return false;
}
else
{
TemplateDeclaration *tdecl = tempdecl->isTemplateDeclaration();
if (errs != global.errors)
errorSupplemental(loc, "while looking for match for %s", toChars());
else if (tdecl && !tdecl->overnext)
{
// Only one template, so we can give better error message
error("does not match template declaration %s", tdecl->toChars());
}
else
::error(loc, "%s %s.%s does not match any template declaration",
tempdecl->kind(), tempdecl->parent->toPrettyChars(), tempdecl->ident->toChars());
return false;
}
/* The best match is td_last
*/
tempdecl = td_last;
return (errs == global.errors);
}
/*****************************************************
* Determine if template instance is really a template function,
* and that template function needs to infer types from the function
* arguments.
*
* Like findBestMatch, iterate possible template candidates,
* but just looks only the necessity of type inference.
*/
bool TemplateInstance::needsTypeInference(Scope *sc, int flag)
{
//printf("TemplateInstance::needsTypeInference() %s\n", toChars());
if (semanticRun != PASSinit)
return false;
struct ParamNeedsInf
{
// context
Scope *sc;
TemplateInstance *ti;
int flag;
// result
Objects dedtypes;
size_t count;
static int fp(void *param, Dsymbol *s)
{
return ((ParamNeedsInf *)param)->fp(s);
}
int fp(Dsymbol *s)
{
TemplateDeclaration *td = s->isTemplateDeclaration();
if (!td)
return 0;
if (td->inuse)
{
td->error(ti->loc, "recursive template expansion");
return 1;
}
/* If any of the overloaded template declarations need inference,
* then return true
*/
FuncDeclaration *fd;
if (!td->onemember)
return 0;
if (TemplateDeclaration *td2 = td->onemember->isTemplateDeclaration())
{
if (!td2->onemember || !td2->onemember->isFuncDeclaration())
return 0;
if (ti->tiargs->length >= td->parameters->length - (td->isVariadic() ? 1 : 0))
return 0;
return 1;
}
if ((fd = td->onemember->isFuncDeclaration()) == NULL ||
fd->type->ty != Tfunction)
{
return 0;
}
for (size_t i = 0; i < td->parameters->length; i++)
{
if ((*td->parameters)[i]->isTemplateThisParameter())
return 1;
}
/* Determine if the instance arguments, tiargs, are all that is necessary
* to instantiate the template.
*/
//printf("tp = %p, td->parameters->length = %d, tiargs->length = %d\n", tp, td->parameters->length, ti->tiargs->length);
TypeFunction *tf = (TypeFunction *)fd->type;
if (size_t dim = tf->parameterList.length())
{
TemplateParameter *tp = td->isVariadic();
if (tp && td->parameters->length > 1)
return 1;
if (!tp && ti->tiargs->length < td->parameters->length)
{
// Can remain tiargs be filled by default arguments?
for (size_t i = ti->tiargs->length; i < td->parameters->length; i++)
{
if (!(*td->parameters)[i]->hasDefaultArg())
return 1;
}
}
for (size_t i = 0; i < dim; i++)
{
// 'auto ref' needs inference.
if (tf->parameterList[i]->storageClass & STCauto)
return 1;
}
}
if (!flag)
{
/* Calculate the need for overload resolution.
* When only one template can match with tiargs, inference is not necessary.
*/
dedtypes.setDim(td->parameters->length);
dedtypes.zero();
if (td->semanticRun == PASSinit)
{
if (td->_scope)
{
// Try to fix forward reference. Ungag errors while doing so.
Ungag ungag = td->ungagSpeculative();
dsymbolSemantic(td, td->_scope);
}
if (td->semanticRun == PASSinit)
{
ti->error("%s forward references template declaration %s", ti->toChars(), td->toChars());
return 1;
}
}
assert(td->semanticRun != PASSinit);
MATCH m = td->matchWithInstance(sc, ti, &dedtypes, NULL, 0);
if (m <= MATCHnomatch)
return 0;
}
/* If there is more than one function template which matches, we may
* need type inference (see Bugzilla 4430)
*/
if (++count > 1)
return 1;
return 0;
}
};
ParamNeedsInf p;
// context
p.ti = this;
p.sc = sc;
p.flag = flag;
// result
p.count = 0;
OverloadSet *tovers = tempdecl->isOverloadSet();
size_t overs_dim = tovers ? tovers->a.length : 1;
unsigned olderrs = global.errors;
for (size_t oi = 0; oi < overs_dim; oi++)
{
if (overloadApply(tovers ? tovers->a[oi] : tempdecl, &p, &ParamNeedsInf::fp))
return true;
}
if (olderrs != global.errors)
{
if (!global.gag)
{
errorSupplemental(loc, "while looking for match for %s", toChars());
semanticRun = PASSsemanticdone;
inst = this;
}
errors = true;
}
//printf("false\n");
return false;
}
/*****************************************
* Determines if a TemplateInstance will need a nested
* generation of the TemplateDeclaration.
* Sets enclosing property if so, and returns != 0;
*/
bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic)
{
int nested = 0;
//printf("TemplateInstance::hasNestedArgs('%s')\n", tempdecl->ident->toChars());
/* A nested instance happens when an argument references a local
* symbol that is on the stack.
*/
for (size_t i = 0; i < args->length; i++)
{
RootObject *o = (*args)[i];
Expression *ea = isExpression(o);
Dsymbol *sa = isDsymbol(o);
Tuple *va = isTuple(o);
if (ea)
{
if (ea->op == TOKvar)
{
sa = ((VarExp *)ea)->var;
goto Lsa;
}
if (ea->op == TOKthis)
{
sa = ((ThisExp *)ea)->var;
goto Lsa;
}
if (ea->op == TOKfunction)
{
if (((FuncExp *)ea)->td)
sa = ((FuncExp *)ea)->td;
else
sa = ((FuncExp *)ea)->fd;
goto Lsa;
}
// Emulate Expression::toMangleBuffer call that had exist in TemplateInstance::genIdent.
if (ea->op != TOKint64 &&
ea->op != TOKfloat64 &&
ea->op != TOKcomplex80 &&
ea->op != TOKnull &&
ea->op != TOKstring &&
ea->op != TOKarrayliteral &&
ea->op != TOKassocarrayliteral &&
ea->op != TOKstructliteral)
{
ea->error("expression %s is not a valid template value argument", ea->toChars());
errors = true;
}
}
else if (sa)
{
Lsa:
sa = sa->toAlias();
TemplateDeclaration *td = sa->isTemplateDeclaration();
if (td)
{
TemplateInstance *ti = sa->toParent()->isTemplateInstance();
if (ti && ti->enclosing)
sa = ti;
}
TemplateInstance *ti = sa->isTemplateInstance();
Declaration *d = sa->isDeclaration();
if ((td && td->literal) ||
(ti && ti->enclosing) ||
(d && !d->isDataseg() &&
!(d->storage_class & STCmanifest) &&
(!d->isFuncDeclaration() || d->isFuncDeclaration()->isNested()) &&
!isTemplateMixin()
))
{
// if module level template
if (isstatic)
{
Dsymbol *dparent = sa->toParent2();
if (!enclosing)
enclosing = dparent;
else if (enclosing != dparent)
{
/* Select the more deeply nested of the two.
* Error if one is not nested inside the other.
*/
for (Dsymbol *p = enclosing; p; p = p->parent)
{
if (p == dparent)
goto L1; // enclosing is most nested
}
for (Dsymbol *p = dparent; p; p = p->parent)
{
if (p == enclosing)
{
enclosing = dparent;
goto L1; // dparent is most nested
}
}
error("%s is nested in both %s and %s",
toChars(), enclosing->toChars(), dparent->toChars());
errors = true;
}
L1:
//printf("\tnested inside %s\n", enclosing->toChars());
nested |= 1;
}
else
{
error("cannot use local `%s` as parameter to non-global template %s", sa->toChars(), tempdecl->toChars());
errors = true;
}
}
}
else if (va)
{
nested |= (int)hasNestedArgs(&va->objects, isstatic);
}
}
//printf("-TemplateInstance::hasNestedArgs('%s') = %d\n", tempdecl->ident->toChars(), nested);
return nested != 0;
}
/*****************************************
* Append 'this' to the specific module members[]
*/
Dsymbols *TemplateInstance::appendToModuleMember()
{
Module *mi = minst; // instantiated -> inserted module
if (global.params.useUnitTests)
{
// Turn all non-root instances to speculative
if (mi && !mi->isRoot())
mi = NULL;
}
//printf("%s->appendToModuleMember() enclosing = %s mi = %s\n",
// toPrettyChars(),
// enclosing ? enclosing->toPrettyChars() : NULL,
// mi ? mi->toPrettyChars() : NULL);
if (!mi || mi->isRoot())
{
/* If the instantiated module is speculative or root, insert to the
* member of a root module. Then:
* - semantic3 pass will get called on the instance members.
* - codegen pass will get a selection chance to do/skip it.
*/
struct N
{
static Dsymbol *getStrictEnclosing(TemplateInstance *ti)
{
do
{
if (ti->enclosing)
return ti->enclosing;
ti = ti->tempdecl->isInstantiated();
}
while (ti);
return NULL;
}
};
Dsymbol *enc = N::getStrictEnclosing(this);
// insert target is made stable by using the module
// where tempdecl is declared.
mi = (enc ? enc : tempdecl)->getModule();
if (!mi->isRoot())
mi = mi->importedFrom;
assert(mi->isRoot());
}
else
{
/* If the instantiated module is non-root, insert to the member of the
* non-root module. Then:
* - semantic3 pass won't be called on the instance.
* - codegen pass won't reach to the instance.
*/
}
//printf("\t--> mi = %s\n", mi->toPrettyChars());
if (memberOf == mi) // already a member
{
return NULL;
}
Dsymbols *a = mi->members;
a->push(this);
memberOf = mi;
if (mi->semanticRun >= PASSsemantic2done && mi->isRoot())
Module::addDeferredSemantic2(this);
if (mi->semanticRun >= PASSsemantic3done && mi->isRoot())
Module::addDeferredSemantic3(this);
return a;
}
/****************************************
* This instance needs an identifier for name mangling purposes.
* Create one by taking the template declaration name and adding
* the type signature for it.
*/
Identifier *TemplateInstance::genIdent(Objects *args)
{
//printf("TemplateInstance::genIdent('%s')\n", tempdecl->ident->toChars());
assert(args == tiargs);
OutBuffer buf;
mangleToBuffer(this, &buf);
//printf("\tgenIdent = %s\n", id);
return Identifier::idPool(buf.peekChars());
}
/*************************************
* Lazily generate identifier for template instance.
* This is because 75% of the ident's are never needed.
*/
Identifier *TemplateInstance::getIdent()
{
if (!ident && inst && !errors)
ident = genIdent(tiargs); // need an identifier for name mangling purposes.
return ident;
}
/****************************************************
* Declare parameters of template instance, initialize them with the
* template instance arguments.
*/
void TemplateInstance::declareParameters(Scope *sc)
{
TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration();
assert(tempdecl);
//printf("TemplateInstance::declareParameters()\n");
for (size_t i = 0; i < tdtypes.length; i++)
{
TemplateParameter *tp = (*tempdecl->parameters)[i];
//RootObject *o = (*tiargs)[i];
RootObject *o = tdtypes[i]; // initializer for tp
//printf("\ttdtypes[%d] = %p\n", i, o);
tempdecl->declareParameter(sc, tp, o);
}
}
/**************************************
* Given an error instantiating the TemplateInstance,
* give the nested TemplateInstance instantiations that got
* us here. Those are a list threaded into the nested scopes.
*/
void TemplateInstance::printInstantiationTrace()
{
if (global.gag)
return;
const unsigned max_shown = 6;
const char format[] = "instantiated from here: %s";
// determine instantiation depth and number of recursive instantiations
unsigned n_instantiations = 1;
unsigned n_totalrecursions = 0;
for (TemplateInstance *cur = this; cur; cur = cur->tinst)
{
++n_instantiations;
// If two instantiations use the same declaration, they are recursive.
// (this works even if they are instantiated from different places in the
// same template).
// In principle, we could also check for multiple-template recursion, but it's
// probably not worthwhile.
if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl
&& cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc))
++n_totalrecursions;
}
// show full trace only if it's short or verbose is on
if (n_instantiations <= max_shown || global.params.verbose)
{
for (TemplateInstance *cur = this; cur; cur = cur->tinst)
{
cur->errors = true;
errorSupplemental(cur->loc, format, cur->toChars());
}
}
else if (n_instantiations - n_totalrecursions <= max_shown)
{
// By collapsing recursive instantiations into a single line,
// we can stay under the limit.
int recursionDepth=0;
for (TemplateInstance *cur = this; cur; cur = cur->tinst)
{
cur->errors = true;
if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl
&& cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc))
{
++recursionDepth;
}
else
{
if (recursionDepth)
errorSupplemental(cur->loc, "%d recursive instantiations from here: %s", recursionDepth+2, cur->toChars());
else
errorSupplemental(cur->loc, format, cur->toChars());
recursionDepth = 0;
}
}
}
else
{
// Even after collapsing the recursions, the depth is too deep.
// Just display the first few and last few instantiations.
unsigned i = 0;
for (TemplateInstance *cur = this; cur; cur = cur->tinst)
{
cur->errors = true;
if (i == max_shown / 2)
errorSupplemental(cur->loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
if (i < max_shown / 2 ||
i >= n_instantiations - max_shown + max_shown / 2)
errorSupplemental(cur->loc, format, cur->toChars());
++i;
}
}
}
Dsymbol *TemplateInstance::toAlias()
{
if (!inst)
{
// Maybe we can resolve it
if (_scope)
{
dsymbolSemantic(this, _scope);
}
if (!inst)
{
error("cannot resolve forward reference");
errors = true;
return this;
}
}
if (inst != this)
return inst->toAlias();
if (aliasdecl)
{
return aliasdecl->toAlias();
}
return inst;
}
const char *TemplateInstance::kind() const
{
return "template instance";
}
bool TemplateInstance::oneMember(Dsymbol **ps, Identifier *)
{
*ps = NULL;
return true;
}
const char *TemplateInstance::toChars()
{
OutBuffer buf;
toCBufferInstance(this, &buf);
return buf.extractChars();
}
const char *TemplateInstance::toPrettyCharsHelper()
{
OutBuffer buf;
toCBufferInstance(this, &buf, true);
return buf.extractChars();
}
/*************************************
* Compare proposed template instantiation with existing template instantiation.
* Note that this is not commutative because of the auto ref check.
* Params:
* this = proposed template instantiation
* o = existing template instantiation
* Returns:
* 0 for match, 1 for no match
*/
int TemplateInstance::compare(RootObject *o)
{
TemplateInstance *ti = (TemplateInstance *)o;
//printf("this = %p, ti = %p\n", this, ti);
assert(tdtypes.length == ti->tdtypes.length);
// Nesting must match
if (enclosing != ti->enclosing)
{
//printf("test2 enclosing %s ti->enclosing %s\n", enclosing ? enclosing->toChars() : "", ti->enclosing ? ti->enclosing->toChars() : "");
goto Lnotequals;
}
//printf("parent = %s, ti->parent = %s\n", parent->toPrettyChars(), ti->parent->toPrettyChars());
if (!arrayObjectMatch(&tdtypes, &ti->tdtypes))
goto Lnotequals;
/* Template functions may have different instantiations based on
* "auto ref" parameters.
*/
if (FuncDeclaration *fd = ti->toAlias()->isFuncDeclaration())
{
if (!fd->errors)
{
ParameterList fparameters = fd->getParameterList();
size_t nfparams = fparameters.length(); // Num function parameters
for (size_t j = 0; j < nfparams; j++)
{
Parameter *fparam = fparameters[j];
if (fparam->storageClass & STCautoref) // if "auto ref"
{
if (!fargs)
goto Lnotequals;
if (fargs->length <= j)
break;
Expression *farg = (*fargs)[j];
if (farg->isLvalue())
{
if (!(fparam->storageClass & STCref))
goto Lnotequals; // auto ref's don't match
}
else
{
if (fparam->storageClass & STCref)
goto Lnotequals; // auto ref's don't match
}
}
}
}
}
return 0;
Lnotequals:
return 1;
}
hash_t TemplateInstance::toHash()
{
if (!hash)
{
hash = (size_t)(void *)enclosing;
hash += arrayObjectHash(&tdtypes);
hash += hash == 0;
}
return hash;
}
/**************************************
* IsExpression can evaluate the specified type speculatively, and even if
* it instantiates any symbols, they are normally unnecessary for the
* final executable.
* However, if those symbols leak to the actual code, compiler should remark
* them as non-speculative to generate their code and link to the final executable.
*/
void unSpeculative(Scope *sc, RootObject *o)
{
if (!o)
return;
if (Tuple *tup = isTuple(o))
{
for (size_t i = 0; i < tup->objects.length; i++)
{
unSpeculative(sc, tup->objects[i]);
}
return;
}
Dsymbol *s = getDsymbol(o);
if (!s)
return;
if (Declaration *d = s->isDeclaration())
{
if (VarDeclaration *vd = d->isVarDeclaration())
o = vd->type;
else if (AliasDeclaration *ad = d->isAliasDeclaration())
{
o = ad->getType();
if (!o)
o = ad->toAlias();
}
else
o = d->toAlias();
s = getDsymbol(o);
if (!s)
return;
}
if (TemplateInstance *ti = s->isTemplateInstance())
{
// If the instance is already non-speculative,
// or it is leaked to the speculative scope.
if (ti->minst != NULL || sc->minst == NULL)
return;
// Remark as non-speculative instance.
ti->minst = sc->minst;
if (!ti->tinst)
ti->tinst = sc->tinst;
unSpeculative(sc, ti->tempdecl);
}
if (TemplateInstance *ti = s->isInstantiated())
unSpeculative(sc, ti);
}
/**
Returns: true if the instances' innards are discardable.
The idea of this function is to see if the template instantiation
can be 100% replaced with its eponymous member. All other members
can be discarded, even in the compiler to free memory (for example,
the template could be expanded in a region allocator, deemed trivial,
the end result copied back out independently and the entire region freed),
and can be elided entirely from the binary.
The current implementation affects code that generally looks like:
---
template foo(args...) {
some_basic_type_or_string helper() { .... }
enum foo = helper();
}
---
since it was the easiest starting point of implementation but it can and
should be expanded more later.
*/
static bool isDiscardable(TemplateInstance *ti)
{
if (ti->aliasdecl == NULL)
return false;
VarDeclaration *v = ti->aliasdecl->isVarDeclaration();
if (v == NULL)
return false;
if (!(v->storage_class & STCmanifest))
return false;
// Currently only doing basic types here because it is the easiest proof-of-concept
// implementation with minimal risk of side effects, but it could likely be
// expanded to any type that already exists outside this particular instance.
if (!(v->type->equals(Type::tstring) || (v->type->isTypeBasic() != NULL)))
return false;
// Static ctors and dtors, even in an eponymous enum template, are still run,
// so if any of them are in here, we'd better not assume it is trivial lest
// we break useful code
for (size_t i = 0; i < ti->members->length; i++)
{
Dsymbol *member = (*ti->members)[i];
if (member->hasStaticCtorOrDtor())
return false;
if (member->isStaticDtorDeclaration())
return false;
if (member->isStaticCtorDeclaration())
return false;
}
// but if it passes through this gauntlet... it should be fine. D code will
// see only the eponymous member, outside stuff can never access it, even through
// reflection; the outside world ought to be none the wiser. Even dmd should be
// able to simply free the memory of everything except the final result.
return true;
}
/***********************************************
* Returns true if this is not instantiated in non-root module, and
* is a part of non-speculative instantiatiation.
*
* Note: minst does not stabilize until semantic analysis is completed,
* so don't call this function during semantic analysis to return precise result.
*/
bool TemplateInstance::needsCodegen()
{
if (!minst)
{
// If this is a speculative instantiation,
// 1. do codegen if ancestors really needs codegen.
// 2. become non-speculative if siblings are not speculative
TemplateInstance *tnext = this->tnext;
TemplateInstance *tinst = this->tinst;
// At first, disconnect chain first to prevent infinite recursion.
this->tnext = NULL;
this->tinst = NULL;
// Determine necessity of tinst before tnext.
if (tinst && tinst->needsCodegen())
{
minst = tinst->minst; // cache result
if (global.params.allInst && minst)
{
return true;
}
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
}
if (tnext && (tnext->needsCodegen() || tnext->minst))
{
minst = tnext->minst; // cache result
if (global.params.allInst && minst)
{
return true;
}
assert(minst);
return minst->isRoot() || minst->rootImports();
}
// Elide codegen because this is really speculative.
return false;
}
if (global.params.allInst)
{
return true;
}
if (isDiscardable(this))
{
return false;
}
/* Even when this is reached to the codegen pass,
* a non-root nested template should not generate code,
* due to avoid ODR violation.
*/
if (enclosing && enclosing->inNonRoot())
{
if (tinst)
{
bool r = tinst->needsCodegen();
minst = tinst->minst; // cache result
return r;
}
if (tnext)
{
bool r = tnext->needsCodegen();
minst = tnext->minst; // cache result
return r;
}
return false;
}
if (global.params.useUnitTests)
{
// Prefer instantiations from root modules, to maximize link-ability.
if (minst->isRoot())
return true;
TemplateInstance *tnext = this->tnext;
TemplateInstance *tinst = this->tinst;
this->tnext = NULL;
this->tinst = NULL;
if (tinst && tinst->needsCodegen())
{
minst = tinst->minst; // cache result
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
}
if (tnext && tnext->needsCodegen())
{
minst = tnext->minst; // cache result
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
}
// Bugzilla 2500 case
if (minst->rootImports())
return true;
// Elide codegen because this is not included in root instances.
return false;
}
else
{
// Prefer instantiations from non-root module, to minimize object code size.
/* If a TemplateInstance is ever instantiated by non-root modules,
* we do not have to generate code for it,
* because it will be generated when the non-root module is compiled.
*
* But, if the non-root 'minst' imports any root modules, it might still need codegen.
*
* The problem is if A imports B, and B imports A, and both A
* and B instantiate the same template, does the compilation of A
* or the compilation of B do the actual instantiation?
*
* See Bugzilla 2500.
*/
if (!minst->isRoot() && !minst->rootImports())
return false;
TemplateInstance *tnext = this->tnext;
this->tnext = NULL;
if (tnext && !tnext->needsCodegen() && tnext->minst)
{
minst = tnext->minst; // cache result
assert(!minst->isRoot());
return false;
}
// Do codegen because this is not included in non-root instances.
return true;
}
}
/* ======================== TemplateMixin ================================ */
TemplateMixin::TemplateMixin(Loc loc, Identifier *ident, TypeQualified *tqual, Objects *tiargs)
: TemplateInstance(loc, tqual->idents.length ? (Identifier *)tqual->idents[tqual->idents.length - 1]
: ((TypeIdentifier *)tqual)->ident)
{
//printf("TemplateMixin(ident = '%s')\n", ident ? ident->toChars() : "");
this->ident = ident;
this->tqual = tqual;
this->tiargs = tiargs ? tiargs : new Objects();
}
Dsymbol *TemplateMixin::syntaxCopy(Dsymbol *)
{
TemplateMixin *tm = new TemplateMixin(loc, ident,
(TypeQualified *)tqual->syntaxCopy(), tiargs);
return TemplateInstance::syntaxCopy(tm);
}
bool TemplateMixin::findTempDecl(Scope *sc)
{
// Follow qualifications to find the TemplateDeclaration
if (!tempdecl)
{
Expression *e;
Type *t;
Dsymbol *s;
tqual->resolve(loc, sc, &e, &t, &s);
if (!s)
{
error("is not defined");
return false;
}
s = s->toAlias();
tempdecl = s->isTemplateDeclaration();
OverloadSet *os = s->isOverloadSet();
/* If an OverloadSet, look for a unique member that is a template declaration
*/
if (os)
{
Dsymbol *ds = NULL;
for (size_t i = 0; i < os->a.length; i++)
{
Dsymbol *s2 = os->a[i]->isTemplateDeclaration();
if (s2)
{
if (ds)
{
tempdecl = os;
break;
}
ds = s2;
}
}
}
if (!tempdecl)
{
error("%s isn't a template", s->toChars());
return false;
}
}
assert(tempdecl);
struct ParamFwdResTm
{
static int fp(void *param, Dsymbol *s)
{
TemplateDeclaration *td = s->isTemplateDeclaration();
if (!td)
return 0;
TemplateMixin *tm = (TemplateMixin *)param;
if (td->semanticRun == PASSinit)
{
if (td->_scope)
dsymbolSemantic(td, td->_scope);
else
{
tm->semanticRun = PASSinit;
return 1;
}
}
return 0;
}
};
// Look for forward references
OverloadSet *tovers = tempdecl->isOverloadSet();
size_t overs_dim = tovers ? tovers->a.length : 1;
for (size_t oi = 0; oi < overs_dim; oi++)
{
if (overloadApply(tovers ? tovers->a[oi] : tempdecl, (void *)this, &ParamFwdResTm::fp))
return false;
}
return true;
}
const char *TemplateMixin::kind() const
{
return "mixin";
}
bool TemplateMixin::oneMember(Dsymbol **ps, Identifier *ident)
{
return Dsymbol::oneMember(ps, ident);
}
int TemplateMixin::apply(Dsymbol_apply_ft_t fp, void *param)
{
if (_scope) // if fwd reference
dsymbolSemantic(this, NULL); // try to resolve it
if (members)
{
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
if (s)
{
if (s->apply(fp, param))
return 1;
}
}
}
return 0;
}
bool TemplateMixin::hasPointers()
{
//printf("TemplateMixin::hasPointers() %s\n", toChars());
if (members)
{
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
//printf(" s = %s %s\n", s->kind(), s->toChars());
if (s->hasPointers())
{
return true;
}
}
}
return false;
}
void TemplateMixin::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion)
{
//printf("TemplateMixin::setFieldOffset() %s\n", toChars());
if (_scope) // if fwd reference
dsymbolSemantic(this, NULL); // try to resolve it
if (members)
{
for (size_t i = 0; i < members->length; i++)
{
Dsymbol *s = (*members)[i];
//printf("\t%s\n", s->toChars());
s->setFieldOffset(ad, poffset, isunion);
}
}
}
const char *TemplateMixin::toChars()
{
OutBuffer buf;
toCBufferInstance(this, &buf);
return buf.extractChars();
}