aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/dmd/dtemplate.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/dtemplate.d')
-rw-r--r--gcc/d/dmd/dtemplate.d8415
1 files changed, 8415 insertions, 0 deletions
diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d
new file mode 100644
index 0000000..c3503bb
--- /dev/null
+++ b/gcc/d/dmd/dtemplate.d
@@ -0,0 +1,8415 @@
+/**
+ * Defines `TemplateDeclaration`, `TemplateInstance` and a few utilities
+ *
+ * This modules holds the two main template types:
+ * `TemplateDeclaration`, which is the user-provided declaration of a template,
+ * and `TemplateInstance`, which is an instance of a `TemplateDeclaration`
+ * with specific arguments.
+ *
+ * Template_Parameter:
+ * Additionally, the classes for template parameters are defined in this module.
+ * The base class, `TemplateParameter`, is inherited by:
+ * - `TemplateTypeParameter`
+ * - `TemplateThisParameter`
+ * - `TemplateValueParameter`
+ * - `TemplateAliasParameter`
+ * - `TemplateTupleParameter`
+ *
+ * Templates_semantic:
+ * The start of the template instantiation process looks like this:
+ * - A `TypeInstance` or `TypeIdentifier` is encountered.
+ * `TypeInstance` have a bang (e.g. `Foo!(arg)`) while `TypeIdentifier` don't.
+ * - A `TemplateInstance` is instantiated
+ * - Semantic is run on the `TemplateInstance` (see `dmd.dsymbolsem`)
+ * - The `TemplateInstance` search for its `TemplateDeclaration`,
+ * runs semantic on the template arguments and deduce the best match
+ * among the possible overloads.
+ * - The `TemplateInstance` search for existing instances with the same
+ * arguments, and uses it if found.
+ * - Otherwise, the rest of semantic is run on the `TemplateInstance`.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dtemplate.d, _dtemplate.d)
+ * Documentation: https://dlang.org/phobos/dmd_dtemplate.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtemplate.d
+ */
+
+module dmd.dtemplate;
+
+import core.stdc.stdio;
+import core.stdc.string;
+import dmd.aggregate;
+import dmd.aliasthis;
+import dmd.arraytypes;
+import dmd.astenums;
+import dmd.ast_node;
+import dmd.dcast;
+import dmd.dclass;
+import dmd.declaration;
+import dmd.dmangle;
+import dmd.dmodule;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.dsymbolsem;
+import dmd.errors;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.func;
+import dmd.globals;
+import dmd.hdrgen;
+import dmd.id;
+import dmd.identifier;
+import dmd.impcnvtab;
+import dmd.init;
+import dmd.initsem;
+import dmd.mtype;
+import dmd.opover;
+import dmd.root.array;
+import dmd.root.outbuffer;
+import dmd.root.rootobject;
+import dmd.semantic2;
+import dmd.semantic3;
+import dmd.tokens;
+import dmd.typesem;
+import dmd.visitor;
+
+import dmd.templateparamsem;
+
+//debug = FindExistingInstance; // print debug stats of findExistingInstance
+private enum LOG = false;
+
+enum IDX_NOTFOUND = 0x12345678;
+
+pure nothrow @nogc
+{
+
+/********************************************
+ * These functions substitute for dynamic_cast. dynamic_cast does not work
+ * on earlier versions of gcc.
+ */
+extern (C++) inout(Expression) isExpression(inout RootObject o)
+{
+ //return dynamic_cast<Expression *>(o);
+ if (!o || o.dyncast() != DYNCAST.expression)
+ return null;
+ return cast(inout(Expression))o;
+}
+
+extern (C++) inout(Dsymbol) isDsymbol(inout RootObject o)
+{
+ //return dynamic_cast<Dsymbol *>(o);
+ if (!o || o.dyncast() != DYNCAST.dsymbol)
+ return null;
+ return cast(inout(Dsymbol))o;
+}
+
+extern (C++) inout(Type) isType(inout RootObject o)
+{
+ //return dynamic_cast<Type *>(o);
+ if (!o || o.dyncast() != DYNCAST.type)
+ return null;
+ return cast(inout(Type))o;
+}
+
+extern (C++) inout(Tuple) isTuple(inout RootObject o)
+{
+ //return dynamic_cast<Tuple *>(o);
+ if (!o || o.dyncast() != DYNCAST.tuple)
+ return null;
+ return cast(inout(Tuple))o;
+}
+
+extern (C++) inout(Parameter) isParameter(inout RootObject o)
+{
+ //return dynamic_cast<Parameter *>(o);
+ if (!o || o.dyncast() != DYNCAST.parameter)
+ return null;
+ return cast(inout(Parameter))o;
+}
+
+extern (C++) inout(TemplateParameter) isTemplateParameter(inout RootObject o)
+{
+ if (!o || o.dyncast() != DYNCAST.templateparameter)
+ return null;
+ return cast(inout(TemplateParameter))o;
+}
+
+/**************************************
+ * Is this Object an error?
+ */
+extern (C++) bool isError(const RootObject o)
+{
+ if (const t = isType(o))
+ return (t.ty == Terror);
+ if (const e = isExpression(o))
+ return (e.op == TOK.error || !e.type || e.type.ty == Terror);
+ if (const v = isTuple(o))
+ return arrayObjectIsError(&v.objects);
+ const 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(const Objects* args)
+{
+ foreach (const o; *args)
+ {
+ if (isError(o))
+ return true;
+ }
+ return false;
+}
+
+/***********************
+ * Try to get arg as a type.
+ */
+inout(Type) getType(inout RootObject o)
+{
+ inout t = isType(o);
+ if (!t)
+ {
+ if (inout e = isExpression(o))
+ return 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));
+ if (auto ea = isExpression(oarg))
+ {
+ // Try to convert Expression to symbol
+ if (auto ve = ea.isVarExp())
+ return ve.var;
+ else if (auto fe = ea.isFuncExp())
+ return fe.td ? fe.td : fe.fd;
+ else if (auto te = ea.isTemplateExp())
+ return te.td;
+ else if (auto te = ea.isScopeExp())
+ return te.sds;
+ else
+ return null;
+ }
+ else
+ {
+ // Try to convert Type to symbol
+ if (auto ta = isType(oarg))
+ return ta.toDsymbol(null);
+ else
+ return isDsymbol(oarg); // if already a symbol
+ }
+}
+
+
+private Expression getValue(ref Dsymbol s)
+{
+ if (s)
+ {
+ if (VarDeclaration v = s.isVarDeclaration())
+ {
+ if (v.storage_class & STC.manifest)
+ return v.getConstInitializer();
+ }
+ }
+ return null;
+}
+
+/***********************
+ * Try to get value from manifest constant
+ */
+private Expression getValue(Expression e)
+{
+ if (!e)
+ return null;
+ if (auto ve = e.isVarExp())
+ {
+ if (auto v = ve.var.isVarDeclaration())
+ {
+ if (v.storage_class & STC.manifest)
+ {
+ e = v.getConstInitializer();
+ }
+ }
+ }
+ return e;
+}
+
+private Expression getExpression(RootObject o)
+{
+ auto s = isDsymbol(o);
+ return s ? .getValue(s) : .getValue(isExpression(o));
+}
+
+/******************************
+ * If o1 matches o2, return true.
+ * Else, return false.
+ */
+private bool match(RootObject o1, RootObject o2)
+{
+ enum log = false;
+
+ static if (log)
+ {
+ 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 (auto t1 = isType(o1))
+ {
+ auto t2 = isType(o2);
+ if (!t2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\tt1 = %s\n", t1.toChars());
+ printf("\tt2 = %s\n", t2.toChars());
+ }
+ if (!t1.equals(t2))
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+ if (auto e1 = getExpression(o1))
+ {
+ auto e2 = getExpression(o2);
+ if (!e2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\te1 = %s '%s' %s\n", e1.type ? e1.type.toChars() : "null", Token.toChars(e1.op), e1.toChars());
+ printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", 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 && e2.type && e1.type.equals(e2.type)) || !e1.equals(e2))
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+ if (auto s1 = isDsymbol(o1))
+ {
+ auto s2 = isDsymbol(o2);
+ if (!s2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\ts1 = %s \n", s1.kind(), s1.toChars());
+ printf("\ts2 = %s \n", s2.kind(), s2.toChars());
+ }
+ if (!s1.equals(s2))
+ goto Lnomatch;
+ if (s1.parent != s2.parent && !s1.isFuncDeclaration() && !s2.isFuncDeclaration())
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+ if (auto u1 = isTuple(o1))
+ {
+ auto u2 = isTuple(o2);
+ if (!u2)
+ goto Lnomatch;
+
+ static if (log)
+ {
+ printf("\tu1 = %s\n", u1.toChars());
+ printf("\tu2 = %s\n", u2.toChars());
+ }
+ if (!arrayObjectMatch(&u1.objects, &u2.objects))
+ goto Lnomatch;
+
+ goto Lmatch;
+ }
+Lmatch:
+ static if (log)
+ printf("\t. match\n");
+ return true;
+
+Lnomatch:
+ static if (log)
+ printf("\t. nomatch\n");
+ return false;
+}
+
+/************************************
+ * Match an array of them.
+ */
+private bool arrayObjectMatch(Objects* oa1, Objects* oa2)
+{
+ if (oa1 == oa2)
+ return true;
+ if (oa1.dim != oa2.dim)
+ return false;
+ immutable oa1dim = oa1.dim;
+ auto oa1d = (*oa1)[].ptr;
+ auto oa2d = (*oa2)[].ptr;
+ foreach (j; 0 .. oa1dim)
+ {
+ RootObject o1 = oa1d[j];
+ RootObject o2 = oa2d[j];
+ if (!match(o1, o2))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+/************************************
+ * Return hash of Objects.
+ */
+private size_t arrayObjectHash(Objects* oa1)
+{
+ import dmd.root.hash : mixHash;
+
+ size_t hash = 0;
+ foreach (o1; *oa1)
+ {
+ /* Must follow the logic of match()
+ */
+ if (auto t1 = isType(o1))
+ hash = mixHash(hash, cast(size_t)t1.deco);
+ else if (auto e1 = getExpression(o1))
+ hash = mixHash(hash, expressionHash(e1));
+ else if (auto s1 = isDsymbol(o1))
+ {
+ auto fa1 = s1.isFuncAliasDeclaration();
+ if (fa1)
+ s1 = fa1.toAliasFunc();
+ hash = mixHash(hash, mixHash(cast(size_t)cast(void*)s1.getIdent(), cast(size_t)cast(void*)s1.parent));
+ }
+ else if (auto u1 = isTuple(o1))
+ hash = mixHash(hash, arrayObjectHash(&u1.objects));
+ }
+ return hash;
+}
+
+
+/************************************
+ * Computes hash of expression.
+ * Handles all Expression classes and MUST match their equals method,
+ * i.e. e1.equals(e2) implies expressionHash(e1) == expressionHash(e2).
+ */
+private size_t expressionHash(Expression e)
+{
+ import dmd.root.ctfloat : CTFloat;
+ import dmd.root.hash : calcHash, mixHash;
+
+ switch (e.op)
+ {
+ case TOK.int64:
+ return cast(size_t) e.isIntegerExp().getInteger();
+
+ case TOK.float64:
+ return CTFloat.hash(e.isRealExp().value);
+
+ case TOK.complex80:
+ auto ce = e.isComplexExp();
+ return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
+
+ case TOK.identifier:
+ return cast(size_t)cast(void*) e.isIdentifierExp().ident;
+
+ case TOK.null_:
+ return cast(size_t)cast(void*) e.isNullExp().type;
+
+ case TOK.string_:
+ return calcHash(e.isStringExp.peekData());
+
+ case TOK.tuple:
+ {
+ auto te = e.isTupleExp();
+ size_t hash = 0;
+ hash += te.e0 ? expressionHash(te.e0) : 0;
+ foreach (elem; *te.exps)
+ hash = mixHash(hash, expressionHash(elem));
+ return hash;
+ }
+
+ case TOK.arrayLiteral:
+ {
+ auto ae = e.isArrayLiteralExp();
+ size_t hash;
+ foreach (i; 0 .. ae.elements.dim)
+ hash = mixHash(hash, expressionHash(ae[i]));
+ return hash;
+ }
+
+ case TOK.assocArrayLiteral:
+ {
+ auto ae = e.isAssocArrayLiteralExp();
+ size_t hash;
+ foreach (i; 0 .. ae.keys.dim)
+ // reduction needs associative op as keys are unsorted (use XOR)
+ hash ^= mixHash(expressionHash((*ae.keys)[i]), expressionHash((*ae.values)[i]));
+ return hash;
+ }
+
+ case TOK.structLiteral:
+ {
+ auto se = e.isStructLiteralExp();
+ size_t hash;
+ foreach (elem; *se.elements)
+ hash = mixHash(hash, elem ? expressionHash(elem) : 0);
+ return hash;
+ }
+
+ case TOK.variable:
+ return cast(size_t)cast(void*) e.isVarExp().var;
+
+ case TOK.function_:
+ return cast(size_t)cast(void*) e.isFuncExp().fd;
+
+ default:
+ // no custom equals for this expression
+ assert((&e.equals).funcptr is &RootObject.equals);
+ // equals based on identity
+ return cast(size_t)cast(void*) e;
+ }
+}
+
+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;
+}
+
+extern (C++) final class Tuple : RootObject
+{
+ Objects objects;
+
+ extern (D) this() {}
+
+ /**
+ Params:
+ numObjects = The initial number of objects.
+ */
+ extern (D) this(size_t numObjects)
+ {
+ objects.setDim(numObjects);
+ }
+
+ // kludge for template.isType()
+ override DYNCAST dyncast() const
+ {
+ return DYNCAST.tuple;
+ }
+
+ override const(char)* toChars() const
+ {
+ return objects.toChars();
+ }
+}
+
+struct TemplatePrevious
+{
+ TemplatePrevious* prev;
+ Scope* sc;
+ Objects* dedargs;
+}
+
+/***********************************************************
+ * [mixin] template Identifier (parameters) [Constraint]
+ * https://dlang.org/spec/template.html
+ * https://dlang.org/spec/template-mixin.html
+ */
+extern (C++) final class TemplateDeclaration : ScopeDsymbol
+{
+ import dmd.root.array : Array;
+
+ TemplateParameters* parameters; // array of TemplateParameter's
+ TemplateParameters* origParameters; // originals for Ddoc
+
+ Expression constraint;
+
+ // Hash table to look up TemplateInstance's of this TemplateDeclaration
+ TemplateInstance[TemplateInstanceBox] instances;
+
+ TemplateDeclaration overnext; // next overloaded TemplateDeclaration
+ TemplateDeclaration overroot; // first in overnext list
+ FuncDeclaration funcroot; // first function in unified overload list
+
+ Dsymbol onemember; // if !=null then one member of this template
+
+ bool literal; // this template declaration is a literal
+ bool ismixin; // this is a mixin template declaration
+ bool isstatic; // this is static template declaration
+ bool isTrivialAliasSeq; /// matches pattern `template AliasSeq(T...) { alias AliasSeq = T; }`
+ bool isTrivialAlias; /// matches pattern `template Alias(T) { alias Alias = qualifiers(T); }`
+ bool deprecated_; /// this template declaration is deprecated
+ Visibility visibility;
+ int inuse; /// for recursive expansion detection
+
+ // threaded list of previous instantiation attempts on stack
+ TemplatePrevious* previous;
+
+ private Expression lastConstraint; /// the constraint after the last failed evaluation
+ private Array!Expression lastConstraintNegs; /// its negative parts
+ private Objects* lastConstraintTiargs; /// template instance arguments for `lastConstraint`
+
+ extern (D) this(const ref Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false)
+ {
+ super(loc, ident);
+ static if (LOG)
+ {
+ printf("TemplateDeclaration(this = %p, id = '%s')\n", this, ident.toChars());
+ }
+ version (none)
+ {
+ if (parameters)
+ for (int i = 0; i < parameters.dim; i++)
+ {
+ TemplateParameter tp = (*parameters)[i];
+ //printf("\tparameter[%d] = %p\n", i, tp);
+ TemplateTypeParameter ttp = tp.isTemplateTypeParameter();
+ if (ttp)
+ {
+ printf("\tparameter[%d] = %s : %s\n", i, tp.ident.toChars(), ttp.specType ? ttp.specType.toChars() : "");
+ }
+ }
+ }
+ this.parameters = parameters;
+ this.origParameters = parameters;
+ this.constraint = constraint;
+ this.members = decldefs;
+ this.literal = literal;
+ this.ismixin = ismixin;
+ this.isstatic = true;
+ this.visibility = Visibility(Visibility.Kind.undefined);
+
+ // Compute in advance for Ddoc's use
+ // https://issues.dlang.org/show_bug.cgi?id=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;
+
+ auto ad = s.isAliasDeclaration();
+ if (!ad || !ad.type)
+ return;
+
+ auto ti = ad.type.isTypeIdentifier();
+
+ if (!ti || ti.idents.length != 0)
+ return;
+
+ if (auto ttp = (*parameters)[0].isTemplateTupleParameter())
+ {
+ if (ti.ident is ttp.ident &&
+ ti.mod == 0)
+ {
+ //printf("found isTrivialAliasSeq %s %s\n", s.toChars(), ad.type.toChars());
+ isTrivialAliasSeq = true;
+ }
+ }
+ else if (auto ttp = (*parameters)[0].isTemplateTypeParameter())
+ {
+ if (ti.ident is ttp.ident)
+ {
+ //printf("found isTrivialAlias %s %s\n", s.toChars(), ad.type.toChars());
+ isTrivialAlias = true;
+ }
+ }
+ }
+
+ override TemplateDeclaration syntaxCopy(Dsymbol)
+ {
+ //printf("TemplateDeclaration.syntaxCopy()\n");
+ TemplateParameters* p = null;
+ if (parameters)
+ {
+ p = new TemplateParameters(parameters.dim);
+ foreach (i, ref param; *p)
+ param = (*parameters)[i].syntaxCopy();
+ }
+ return new TemplateDeclaration(loc, ident, p, constraint ? constraint.syntaxCopy() : null, Dsymbol.arraySyntaxCopy(members), ismixin, literal);
+ }
+
+ /**********************************
+ * Overload existing TemplateDeclaration 'this' with the new one 's'.
+ * Return true if successful; i.e. no conflict.
+ */
+ override bool overloadInsert(Dsymbol s)
+ {
+ static if (LOG)
+ {
+ printf("TemplateDeclaration.overloadInsert('%s')\n", s.toChars());
+ }
+ FuncDeclaration fd = s.isFuncDeclaration();
+ if (fd)
+ {
+ if (funcroot)
+ return funcroot.overloadInsert(fd);
+ funcroot = fd;
+ return funcroot.overloadInsert(this);
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=15795
+ // if candidate is an alias and its sema is not run then
+ // insertion can fail because the thing it alias is not known
+ if (AliasDeclaration ad = s.isAliasDeclaration())
+ {
+ if (s._scope)
+ aliasSemantic(ad, s._scope);
+ if (ad.aliassym && ad.aliassym is this)
+ return false;
+ }
+ TemplateDeclaration td = s.toAlias().isTemplateDeclaration();
+ if (!td)
+ return false;
+
+ TemplateDeclaration pthis = this;
+ TemplateDeclaration* ptd;
+ for (ptd = &pthis; *ptd; ptd = &(*ptd).overnext)
+ {
+ }
+
+ td.overroot = this;
+ *ptd = td;
+ static if (LOG)
+ {
+ printf("\ttrue: no conflict\n");
+ }
+ return true;
+ }
+
+ override bool hasStaticCtorOrDtor()
+ {
+ return false; // don't scan uninstantiated templates
+ }
+
+ override const(char)* kind() const
+ {
+ return (onemember && onemember.isAggregateDeclaration()) ? onemember.kind() : "template";
+ }
+
+ override const(char)* toChars() const
+ {
+ return toCharsMaybeConstraints(true);
+ }
+
+ /****************************
+ * Similar to `toChars`, but does not print the template constraints
+ */
+ const(char)* toCharsNoConstraints() const
+ {
+ return toCharsMaybeConstraints(false);
+ }
+
+ const(char)* toCharsMaybeConstraints(bool includeConstraints) const
+ {
+ if (literal)
+ return Dsymbol.toChars();
+
+ OutBuffer buf;
+ HdrGenState hgs;
+
+ buf.writestring(ident.toString());
+ buf.writeByte('(');
+ foreach (i, const tp; *parameters)
+ {
+ if (i)
+ buf.writestring(", ");
+ .toCBuffer(tp, &buf, &hgs);
+ }
+ buf.writeByte(')');
+
+ if (onemember)
+ {
+ const FuncDeclaration fd = onemember.isFuncDeclaration();
+ if (fd && fd.type)
+ {
+ TypeFunction tf = cast(TypeFunction)fd.type;
+ buf.writestring(parametersTypeToChars(tf.parameterList));
+ }
+ }
+
+ if (includeConstraints &&
+ constraint)
+ {
+ buf.writestring(" if (");
+ .toCBuffer(constraint, &buf, &hgs);
+ buf.writeByte(')');
+ }
+
+ return buf.extractChars();
+ }
+
+ override Visibility visible() pure nothrow @nogc @safe
+ {
+ return visibility;
+ }
+
+ /****************************
+ * Check to see if constraint is satisfied.
+ */
+ extern (D) bool evaluateConstraint(TemplateInstance ti, Scope* sc, Scope* paramscope, Objects* dedargs, FuncDeclaration fd)
+ {
+ /* Detect recursive attempts to instantiate this template declaration,
+ * https://issues.dlang.org/show_bug.cgi?id=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))
+ continue;
+ //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.
+ * the chain of enclosing scopes is broken by paramscope (its enclosing
+ * scope is _scope, but paramscope.callsc is the instantiating scope). So
+ * it's good enough to check the chain of callsc
+ */
+ for (Scope* scx = paramscope.callsc; scx; scx = scx.callsc)
+ {
+ // The first scx might be identical for nested eponymeous templates, e.g.
+ // template foo() { void foo()() {...} }
+ if (scx == p.sc && scx !is paramscope.callsc)
+ return false;
+ }
+ /* BUG: should also check for ref param differences
+ */
+ }
+
+ TemplatePrevious pr;
+ pr.prev = previous;
+ pr.sc = paramscope.callsc;
+ pr.dedargs = dedargs;
+ previous = &pr; // add this to threaded list
+
+ Scope* scx = paramscope.push(ti);
+ scx.parent = ti;
+ scx.tinst = null;
+ scx.minst = null;
+ // Set SCOPE.constraint before declaring function parameters for the static condition
+ // (previously, this was immediately before calling evalStaticCondition), so the
+ // semantic pass knows not to issue deprecation warnings for these throw-away decls.
+ // https://issues.dlang.org/show_bug.cgi?id=21831
+ scx.flags |= SCOPE.constraint;
+
+ 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
+ */
+ auto tf = fd.type.isTypeFunction();
+
+ scx.parent = fd;
+
+ Parameters* fparameters = tf.parameterList.parameters;
+ const nfparams = tf.parameterList.length;
+ foreach (i, fparam; tf.parameterList)
+ {
+ fparam.storageClass &= (STC.IOR | STC.lazy_ | STC.final_ | STC.TYPECTOR | STC.nodtor);
+ fparam.storageClass |= STC.parameter;
+ if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nfparams)
+ {
+ fparam.storageClass |= STC.variadic;
+ /* Don't need to set STC.scope_ because this will only
+ * be evaluated at compile time
+ */
+ }
+ }
+ foreach (fparam; *fparameters)
+ {
+ if (!fparam.ident)
+ continue;
+ // don't add it, if it has no name
+ auto v = new VarDeclaration(loc, fparam.type, fparam.ident, null);
+ fparam.storageClass |= STC.parameter;
+ v.storage_class = fparam.storageClass;
+ v.dsymbolSemantic(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 |= STC.static_;
+ fd.declareThis(scx);
+ }
+
+ lastConstraint = constraint.syntaxCopy();
+ lastConstraintTiargs = ti.tiargs;
+ lastConstraintNegs.setDim(0);
+
+ import dmd.staticcond;
+
+ assert(ti.inst is null);
+ ti.inst = ti; // temporary instantiation to enable genIdent()
+ bool errors;
+ const bool result = evalStaticCondition(scx, constraint, lastConstraint, errors, &lastConstraintNegs);
+ if (result || errors)
+ {
+ lastConstraint = null;
+ lastConstraintTiargs = null;
+ lastConstraintNegs.setDim(0);
+ }
+ ti.inst = null;
+ ti.symtab = null;
+ scx = scx.pop();
+ previous = pr.prev; // unlink from threaded list
+ if (errors)
+ return false;
+ return result;
+ }
+
+ /****************************
+ * Destructively get the error message from the last constraint evaluation
+ * Params:
+ * tip = tip to show after printing all overloads
+ */
+ const(char)* getConstraintEvalError(ref const(char)* tip)
+ {
+ import dmd.staticcond;
+
+ // there will be a full tree view in verbose mode, and more compact list in the usual
+ const full = global.params.verbose;
+ uint count;
+ const msg = visualizeStaticCondition(constraint, lastConstraint, lastConstraintNegs[], full, count);
+ scope (exit)
+ {
+ lastConstraint = null;
+ lastConstraintTiargs = null;
+ lastConstraintNegs.setDim(0);
+ }
+ if (!msg)
+ return null;
+
+ OutBuffer buf;
+
+ assert(parameters && lastConstraintTiargs);
+ if (parameters.length > 0)
+ {
+ formatParamsWithTiargs(*lastConstraintTiargs, buf);
+ buf.writenl();
+ }
+ if (!full)
+ {
+ // choosing singular/plural
+ const s = (count == 1) ?
+ " must satisfy the following constraint:" :
+ " must satisfy one of the following constraints:";
+ buf.writestring(s);
+ buf.writenl();
+ // the constraints
+ buf.writeByte('`');
+ buf.writestring(msg);
+ buf.writeByte('`');
+ }
+ else
+ {
+ buf.writestring(" whose parameters have the following constraints:");
+ buf.writenl();
+ const sep = " `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`";
+ buf.writestring(sep);
+ buf.writenl();
+ // the constraints
+ buf.writeByte('`');
+ buf.writestring(msg);
+ buf.writeByte('`');
+ buf.writestring(sep);
+ tip = "not satisfied constraints are marked with `>`";
+ }
+ return buf.extractChars();
+ }
+
+ private void formatParamsWithTiargs(ref Objects tiargs, ref OutBuffer buf)
+ {
+ buf.writestring(" with `");
+
+ // write usual arguments line-by-line
+ // skips trailing default ones - they are not present in `tiargs`
+ const bool variadic = isVariadic() !is null;
+ const end = cast(int)parameters.length - (variadic ? 1 : 0);
+ uint i;
+ for (; i < tiargs.length && i < end; i++)
+ {
+ if (i > 0)
+ {
+ buf.writeByte(',');
+ buf.writenl();
+ buf.writestring(" ");
+ }
+ buf.write((*parameters)[i]);
+ buf.writestring(" = ");
+ buf.write(tiargs[i]);
+ }
+ // write remaining variadic arguments on the last line
+ if (variadic)
+ {
+ if (i > 0)
+ {
+ buf.writeByte(',');
+ buf.writenl();
+ buf.writestring(" ");
+ }
+ buf.write((*parameters)[end]);
+ buf.writestring(" = ");
+ buf.writeByte('(');
+ if (cast(int)tiargs.length - end > 0)
+ {
+ buf.write(tiargs[end]);
+ foreach (j; parameters.length .. tiargs.length)
+ {
+ buf.writestring(", ");
+ buf.write(tiargs[j]);
+ }
+ }
+ buf.writeByte(')');
+ }
+ buf.writeByte('`');
+ }
+
+ /******************************
+ * Create a scope for the parameters of the TemplateInstance
+ * `ti` in the parent scope sc from the ScopeDsymbol paramsym.
+ *
+ * If paramsym is null a new ScopeDsymbol is used in place of
+ * paramsym.
+ * Params:
+ * ti = the TemplateInstance whose parameters to generate the scope for.
+ * sc = the parent scope of ti
+ * Returns:
+ * a scope for the parameters of ti
+ */
+ Scope* scopeForTemplateParameters(TemplateInstance ti, Scope* sc)
+ {
+ 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;
+ return paramscope;
+ }
+
+ /***************************************
+ * 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.
+ */
+ extern (D) MATCH matchWithInstance(Scope* sc, TemplateInstance ti, Objects* dedtypes, Expressions* fargs, int flag)
+ {
+ enum LOGM = 0;
+ static if (LOGM)
+ {
+ printf("\n+TemplateDeclaration.matchWithInstance(this = %s, ti = %s, flag = %d)\n", toChars(), ti.toChars(), flag);
+ }
+ version (none)
+ {
+ printf("dedtypes.dim = %d, parameters.dim = %d\n", dedtypes.dim, parameters.dim);
+ if (ti.tiargs.dim)
+ printf("ti.tiargs.dim = %d, [0] = %p\n", ti.tiargs.dim, (*ti.tiargs)[0]);
+ }
+ MATCH nomatch()
+ {
+ static if (LOGM)
+ {
+ printf(" no match\n");
+ }
+ return MATCH.nomatch;
+ }
+ MATCH m;
+ size_t dedtypes_dim = dedtypes.dim;
+
+ dedtypes.zero();
+
+ if (errors)
+ return MATCH.nomatch;
+
+ size_t parameters_dim = parameters.dim;
+ int variadic = isVariadic() !is null;
+
+ // If more arguments than parameters, no match
+ if (ti.tiargs.dim > parameters_dim && !variadic)
+ {
+ static if (LOGM)
+ {
+ printf(" no match: more arguments than parameters\n");
+ }
+ return MATCH.nomatch;
+ }
+
+ assert(dedtypes_dim == parameters_dim);
+ assert(dedtypes_dim >= ti.tiargs.dim || variadic);
+
+ assert(_scope);
+
+ // Set up scope for template parameters
+ Scope* paramscope = scopeForTemplateParameters(ti,sc);
+
+ // Attempt type deduction
+ m = MATCH.exact;
+ for (size_t i = 0; i < dedtypes_dim; i++)
+ {
+ MATCH m2;
+ TemplateParameter tp = (*parameters)[i];
+ Declaration sparam;
+
+ //printf("\targument [%d]\n", i);
+ static if (LOGM)
+ {
+ //printf("\targument [%d] is %s\n", i, oarg ? oarg.toChars() : "null");
+ TemplateTypeParameter ttp = tp.isTemplateTypeParameter();
+ if (ttp)
+ printf("\tparameter[%d] is %s : %s\n", i, tp.ident.toChars(), ttp.specType ? ttp.specType.toChars() : "");
+ }
+
+ inuse++;
+ m2 = tp.matchArg(ti.loc, paramscope, ti.tiargs, i, parameters, dedtypes, &sparam);
+ inuse--;
+ //printf("\tm2 = %d\n", m2);
+ if (m2 == MATCH.nomatch)
+ {
+ version (none)
+ {
+ printf("\tmatchArg() for parameter %i failed\n", i);
+ }
+ return nomatch();
+ }
+
+ if (m2 < m)
+ m = m2;
+
+ if (!flag)
+ sparam.dsymbolSemantic(paramscope);
+ if (!paramscope.insert(sparam)) // TODO: This check can make more early
+ {
+ // in TemplateDeclaration.semantic, and
+ // then we don't need to make sparam if flags == 0
+ return nomatch();
+ }
+ }
+
+ if (!flag)
+ {
+ /* Any parameter left without a type gets the type of
+ * its corresponding arg
+ */
+ foreach (i, ref dedtype; *dedtypes)
+ {
+ if (!dedtype)
+ {
+ assert(i < ti.tiargs.dim);
+ dedtype = cast(Type)(*ti.tiargs)[i];
+ }
+ }
+ }
+
+ if (m > MATCH.nomatch && 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)
+ {
+ TypeFunction tf = fd.type.isTypeFunction().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.
+ foreach (ref param; *tf.parameterList.parameters)
+ param.defaultArg = null;
+
+ tf.next = null;
+ tf.incomplete = true;
+
+ // Resolve parameter types and 'auto ref's.
+ tf.fargs = fargs;
+ uint olderrors = global.startGagging();
+ fd.type = tf.typeSemantic(loc, paramscope);
+ global.endGagging(olderrors);
+ if (fd.type.ty != Tfunction)
+ return nomatch();
+ fd.originalType = fd.type; // for mangling
+ }
+
+ // TODO: dedtypes => ti.tiargs ?
+ if (!evaluateConstraint(ti, sc, paramscope, dedtypes, fd))
+ return nomatch();
+ }
+
+ static if (LOGM)
+ {
+ // Print out the results
+ printf("--------------------------\n");
+ printf("template %s\n", toChars());
+ printf("instance %s\n", ti.toChars());
+ if (m > MATCH.nomatch)
+ {
+ for (size_t i = 0; i < dedtypes_dim; i++)
+ {
+ TemplateParameter tp = (*parameters)[i];
+ RootObject oarg;
+ printf(" [%d]", i);
+ if (i < ti.tiargs.dim)
+ oarg = (*ti.tiargs)[i];
+ else
+ oarg = null;
+ tp.print(oarg, (*dedtypes)[i]);
+ }
+ }
+ else
+ return nomatch();
+ }
+ static if (LOGM)
+ {
+ printf(" match = %d\n", m);
+ }
+
+ paramscope.pop();
+ static if (LOGM)
+ {
+ printf("-TemplateDeclaration.matchWithInstance(this = %p, ti = %p) = %d\n", this, ti, m);
+ }
+ 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 leastAsSpecialized(Scope* sc, TemplateDeclaration td2, Expressions* fargs)
+ {
+ enum LOG_LEASTAS = 0;
+ static if (LOG_LEASTAS)
+ {
+ printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars());
+ }
+
+ /* 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.
+ */
+
+ // Set type arguments to dummy template instance to be types
+ // generated from the parameters to this template declaration
+ auto tiargs = new Objects();
+ tiargs.reserve(parameters.dim);
+ foreach (tp; *parameters)
+ {
+ if (tp.dependent)
+ break;
+ RootObject p = tp.dummyArg();
+ if (!p) //TemplateTupleParameter
+ break;
+
+ tiargs.push(p);
+ }
+ scope TemplateInstance ti = new TemplateInstance(Loc.initial, ident, tiargs); // create dummy template instance
+
+ // Temporary Array to hold deduced types
+ Objects dedtypes = Objects(td2.parameters.dim);
+
+ // Attempt a type deduction
+ MATCH m = td2.matchWithInstance(sc, ti, &dedtypes, fargs, 1);
+ if (m > MATCH.nomatch)
+ {
+ /* A non-variadic template is more specialized than a
+ * variadic one.
+ */
+ TemplateTupleParameter tp = isVariadic();
+ if (tp && !tp.dependent && !td2.isVariadic())
+ goto L1;
+
+ static if (LOG_LEASTAS)
+ {
+ printf(" matches %d, so is least as specialized\n", m);
+ }
+ return m;
+ }
+ L1:
+ static if (LOG_LEASTAS)
+ {
+ printf(" doesn't match, so is not as specialized\n");
+ }
+ return MATCH.nomatch;
+ }
+
+ /*************************************************
+ * 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 pair of initial and inferred template arguments
+ */
+ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateInstance ti, Scope* sc, ref 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 = MATCH.exact;
+ MATCH matchTiargs = MATCH.exact;
+ ParameterList fparameters; // function parameter list
+ VarArg fvarargs; // function varargs
+ uint wildmatch = 0;
+ size_t inferStart = 0;
+
+ Loc instLoc = ti.loc;
+ Objects* tiargs = ti.tiargs;
+ auto dedargs = new Objects();
+ Objects* dedtypes = &ti.tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T
+
+ version (none)
+ {
+ printf("\nTemplateDeclaration.deduceFunctionTemplateMatch() %s\n", toChars());
+ for (size_t i = 0; i < (fargs ? fargs.dim : 0); i++)
+ {
+ Expression e = (*fargs)[i];
+ printf("\tfarg[%d] is %s, type is %s\n", i, e.toChars(), e.type.toChars());
+ }
+ printf("fd = %s\n", fd.toChars());
+ printf("fd.type = %s\n", fd.type.toChars());
+ if (tthis)
+ printf("tthis = %s\n", tthis.toChars());
+ }
+
+ assert(_scope);
+
+ dedargs.setDim(parameters.dim);
+ dedargs.zero();
+
+ dedtypes.setDim(parameters.dim);
+ dedtypes.zero();
+
+ if (errors || fd.errors)
+ return MATCHpair(MATCH.nomatch, MATCH.nomatch);
+
+ // Set up scope for parameters
+ Scope* paramscope = scopeForTemplateParameters(ti,sc);
+
+ MATCHpair nomatch()
+ {
+ paramscope.pop();
+ //printf("\tnomatch\n");
+ return MATCHpair(MATCH.nomatch, MATCH.nomatch);
+ }
+
+ MATCHpair matcherror()
+ {
+ // todo: for the future improvement
+ paramscope.pop();
+ //printf("\terror\n");
+ return MATCHpair(MATCH.nomatch, MATCH.nomatch);
+ }
+ // Mark the parameter scope as deprecated if the templated
+ // function is deprecated (since paramscope.enclosing is the
+ // calling scope already)
+ paramscope.stc |= fd.storage_class & STC.deprecated_;
+
+ TemplateTupleParameter tp = isVariadic();
+ Tuple declaredTuple = null;
+
+ version (none)
+ {
+ for (size_t i = 0; i < dedargs.dim; i++)
+ {
+ printf("\tdedarg[%d] = ", i);
+ RootObject oarg = (*dedargs)[i];
+ if (oarg)
+ printf("%s", oarg.toChars());
+ printf("\n");
+ }
+ }
+
+ ntargs = 0;
+ if (tiargs)
+ {
+ // Set initial template arguments
+ ntargs = tiargs.dim;
+ size_t n = parameters.dim;
+ if (tp)
+ n--;
+ if (ntargs > n)
+ {
+ if (!tp)
+ return nomatch();
+
+ /* The extra initial template arguments
+ * now form the tuple argument.
+ */
+ auto t = new Tuple(ntargs - n);
+ assert(parameters.dim);
+ (*dedargs)[parameters.dim - 1] = t;
+
+ for (size_t i = 0; i < t.objects.dim; i++)
+ {
+ t.objects[i] = (*tiargs)[n + i];
+ }
+ declareParameter(paramscope, tp, t);
+ declaredTuple = t;
+ }
+ else
+ n = ntargs;
+
+ memcpy(dedargs.tdata(), tiargs.tdata(), n * (*dedargs.tdata()).sizeof);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ assert(i < parameters.dim);
+ Declaration sparam = null;
+ MATCH m = (*parameters)[i].matchArg(instLoc, paramscope, dedargs, i, parameters, dedtypes, &sparam);
+ //printf("\tdeduceType m = %d\n", m);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < matchTiargs)
+ matchTiargs = m;
+
+ sparam.dsymbolSemantic(paramscope);
+ if (!paramscope.insert(sparam))
+ return nomatch();
+ }
+ if (n < parameters.dim && !declaredTuple)
+ {
+ inferStart = n;
+ }
+ else
+ inferStart = parameters.dim;
+ //printf("tiargs matchTiargs = %d\n", matchTiargs);
+ }
+ version (none)
+ {
+ for (size_t i = 0; i < dedargs.dim; i++)
+ {
+ printf("\tdedarg[%d] = ", i);
+ RootObject oarg = (*dedargs)[i];
+ if (oarg)
+ printf("%s", oarg.toChars());
+ printf("\n");
+ }
+ }
+
+ fparameters = fd.getParameterList();
+ nfparams = fparameters.length; // number of function parameters
+ nfargs = fargs ? fargs.dim : 0; // number of function arguments
+
+ /* Check for match of function arguments with variadic template
+ * parameter, such as:
+ *
+ * void foo(T, A...)(T t, A a);
+ * void main() { foo(1,2,3); }
+ */
+ if (tp) // if variadic
+ {
+ // TemplateTupleParameter always makes most lesser matching.
+ matchTiargs = MATCH.convert;
+
+ if (nfparams == 0 && nfargs != 0) // if no function parameters
+ {
+ if (!declaredTuple)
+ {
+ auto t = new Tuple();
+ //printf("t = %p\n", t);
+ (*dedargs)[parameters.dim - 1] = t;
+ declareParameter(paramscope, tp, t);
+ declaredTuple = t;
+ }
+ }
+ else
+ {
+ /* Figure out which of the function parameters matches
+ * the tuple template parameter. Do this by matching
+ * type identifiers.
+ * Set the index of this function parameter to fptupindex.
+ */
+ for (fptupindex = 0; fptupindex < nfparams; fptupindex++)
+ {
+ auto fparam = (*fparameters.parameters)[fptupindex]; // fparameters[fptupindex] ?
+ if (fparam.type.ty != Tident)
+ continue;
+ TypeIdentifier tid = cast(TypeIdentifier)fparam.type;
+ if (!tp.ident.equals(tid.ident) || tid.idents.dim)
+ continue;
+
+ if (fparameters.varargs != VarArg.none) // variadic function doesn't
+ return nomatch(); // go with variadic template
+
+ goto L1;
+ }
+ fptupindex = IDX_NOTFOUND;
+ L1:
+ }
+ }
+
+ if (toParent().isModule() || (_scope.stc & STC.static_))
+ tthis = null;
+ if (tthis)
+ {
+ bool hasttp = false;
+
+ // Match 'tthis' to any TemplateThisParameter's
+ foreach (param; *parameters)
+ {
+ if (auto ttp = param.isTemplateThisParameter())
+ {
+ hasttp = true;
+
+ Type t = new TypeIdentifier(Loc.initial, ttp.ident);
+ MATCH m = deduceType(tthis, paramscope, t, parameters, dedtypes);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ 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, https://issues.dlang.org/show_bug.cgi?id=5504
+ Dsymbol p = parent;
+ while (p.isTemplateDeclaration() || p.isTemplateInstance())
+ p = p.parent;
+ AggregateDeclaration ad = p.isAggregateDeclaration();
+ if (ad)
+ stc |= ad.storage_class;
+
+ ubyte mod = fd.type.mod;
+ if (stc & STC.immutable_)
+ mod = MODFlags.immutable_;
+ else
+ {
+ if (stc & (STC.shared_ | STC.synchronized_))
+ mod |= MODFlags.shared_;
+ if (stc & STC.const_)
+ mod |= MODFlags.const_;
+ if (stc & STC.wild)
+ mod |= MODFlags.wild;
+ }
+
+ ubyte thismod = tthis.mod;
+ if (hasttp)
+ mod = MODmerge(thismod, mod);
+ MATCH m = MODmethodConv(thismod, mod);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < match)
+ match = m;
+ }
+ }
+
+ // Loop through the function parameters
+ {
+ //printf("%s\n\tnfargs = %d, nfparams = %d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple.objects.dim : 0);
+ //printf("\ttp = %p, fptupindex = %d, found = %d, declaredTuple = %s\n", tp, fptupindex, fptupindex != IDX_NOTFOUND, declaredTuple ? declaredTuple.toChars() : NULL);
+ size_t argi = 0;
+ size_t nfargs2 = nfargs; // nfargs + supplied defaultArgs
+ for (size_t parami = 0; parami < nfparams; parami++)
+ {
+ Parameter fparam = 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 = cast(TypeIdentifier)prmtype;
+ if (!declaredTuple)
+ {
+ /* The types of the function arguments
+ * now form the tuple argument.
+ */
+ declaredTuple = new Tuple();
+ (*dedargs)[parameters.dim - 1] = declaredTuple;
+
+ /* Count function parameters with no defaults following a tuple parameter.
+ * void foo(U, T...)(int y, T, U, double, int bar = 0) {} // rem == 2 (U, double)
+ */
+ size_t rem = 0;
+ for (size_t j = parami + 1; j < nfparams; j++)
+ {
+ Parameter p = fparameters[j];
+ if (p.defaultArg)
+ {
+ break;
+ }
+ if (!reliesOnTemplateParameters(p.type, (*parameters)[inferStart .. parameters.dim]))
+ {
+ Type pt = p.type.syntaxCopy().typeSemantic(fd.loc, paramscope);
+ rem += pt.ty == Ttuple ? (cast(TypeTuple)pt).arguments.dim : 1;
+ }
+ else
+ {
+ ++rem;
+ }
+ }
+
+ if (nfargs2 - argi < rem)
+ return nomatch();
+ declaredTuple.objects.setDim(nfargs2 - argi - rem);
+ for (size_t i = 0; i < declaredTuple.objects.dim; i++)
+ {
+ farg = (*fargs)[argi + i];
+
+ // Check invalid arguments to detect errors early.
+ if (farg.op == TOK.error || farg.type.ty == Terror)
+ return nomatch();
+
+ if (!(fparam.storageClass & STC.lazy_) && farg.type.ty == Tvoid)
+ return nomatch();
+
+ Type tt;
+ MATCH m;
+ if (ubyte wm = deduceWildHelper(farg.type, &tt, tid))
+ {
+ wildmatch |= wm;
+ m = MATCH.constant;
+ }
+ else
+ {
+ m = deduceTypeHelper(farg.type, &tt, tid);
+ }
+ if (m == MATCH.nomatch)
+ return nomatch();
+ 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 & STC.ref_) || (fparam.storageClass & STC.auto_) && !farg.isLvalue()))
+ {
+ tt = tt.mutableOf();
+ }
+ declaredTuple.objects[i] = tt;
+ }
+ declareParameter(paramscope, tp, declaredTuple);
+ }
+ else
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=6810
+ // If declared tuple is not a type tuple,
+ // it cannot be function parameter types.
+ for (size_t i = 0; i < declaredTuple.objects.dim; i++)
+ {
+ if (!isType(declaredTuple.objects[i]))
+ return nomatch();
+ }
+ }
+ assert(declaredTuple);
+ argi += declaredTuple.objects.dim;
+ continue;
+ }
+
+ // If parameter type doesn't depend on inferred template parameters,
+ // semantic it to get actual type.
+ if (!reliesOnTemplateParameters(prmtype, (*parameters)[inferStart .. parameters.dim]))
+ {
+ // should copy prmtype to avoid affecting semantic result
+ prmtype = prmtype.syntaxCopy().typeSemantic(fd.loc, paramscope);
+
+ if (prmtype.ty == Ttuple)
+ {
+ TypeTuple tt = cast(TypeTuple)prmtype;
+ size_t tt_dim = tt.arguments.dim;
+ for (size_t j = 0; j < tt_dim; j++, ++argi)
+ {
+ Parameter p = (*tt.arguments)[j];
+ if (j == tt_dim - 1 && fparameters.varargs == VarArg.typesafe &&
+ parami + 1 == nfparams && argi < nfargs)
+ {
+ prmtype = p.type;
+ goto Lvarargs;
+ }
+ if (argi >= nfargs)
+ {
+ if (p.defaultArg)
+ continue;
+
+ // https://issues.dlang.org/show_bug.cgi?id=19888
+ if (fparam.defaultArg)
+ break;
+
+ return nomatch();
+ }
+ farg = (*fargs)[argi];
+ if (!farg.implicitConvTo(p.type))
+ return nomatch();
+ }
+ continue;
+ }
+ }
+
+ if (argi >= nfargs) // if not enough arguments
+ {
+ if (!fparam.defaultArg)
+ goto Lvarargs;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=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)
+ {
+ foreach (ref dedtype; *dedtypes)
+ {
+ Type at = isType(dedtype);
+ if (at && at.ty == Tnone)
+ {
+ TypeDeduced xt = cast(TypeDeduced)at;
+ dedtype = xt.tded; // 'unbox'
+ }
+ }
+ for (size_t i = ntargs; i < dedargs.dim; i++)
+ {
+ TemplateParameter tparam = (*parameters)[i];
+
+ RootObject oarg = (*dedargs)[i];
+ RootObject oded = (*dedtypes)[i];
+ if (oarg)
+ continue;
+
+ 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 == MATCH.nomatch)
+ return nomatch();
+ 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 (MATCH.convert < matchTiargs)
+ matchTiargs = MATCH.convert;
+ }
+ (*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 = farg.expressionSemantic(paramscope);
+ farg = resolveProperties(paramscope, farg);
+ }
+ else
+ {
+ farg = (*fargs)[argi];
+ }
+ {
+ // Check invalid arguments to detect errors early.
+ if (farg.op == TOK.error || farg.type.ty == Terror)
+ return nomatch();
+
+ Type att = null;
+ Lretry:
+ version (none)
+ {
+ printf("\tfarg.type = %s\n", farg.type.toChars());
+ printf("\tfparam.type = %s\n", prmtype.toChars());
+ }
+ Type argtype = farg.type;
+
+ if (!(fparam.storageClass & STC.lazy_) && argtype.ty == Tvoid && farg.op != TOK.function_)
+ return nomatch();
+
+ // https://issues.dlang.org/show_bug.cgi?id=12876
+ // Optimize argument to allow CT-known length matching
+ farg = farg.optimize(WANTvalue, fparam.isReference());
+ //printf("farg = %s %s\n", farg.type.toChars(), farg.toChars());
+
+ RootObject oarg = farg;
+ if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || 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 = (cast(TypeAArray)prmtype).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ if (farg.op == TOK.string_)
+ {
+ StringExp se = cast(StringExp)farg;
+ argtype = se.type.nextOf().sarrayOf(se.len);
+ }
+ else if (farg.op == TOK.arrayLiteral)
+ {
+ ArrayLiteralExp ae = cast(ArrayLiteralExp)farg;
+ argtype = ae.type.nextOf().sarrayOf(ae.elements.dim);
+ }
+ else if (farg.op == TOK.slice)
+ {
+ SliceExp se = cast(SliceExp)farg;
+ if (Type tsa = toStaticArrayType(se))
+ argtype = tsa;
+ }
+ }
+
+ oarg = argtype;
+ }
+ else if ((fparam.storageClass & STC.out_) == 0 && (argtype.ty == Tarray || argtype.ty == Tpointer) && templateParameterLookup(prmtype, parameters) != IDX_NOTFOUND && (cast(TypeIdentifier)prmtype).idents.dim == 0)
+ {
+ /* The farg passing to the prmtype always make a copy. Therefore,
+ * we can shrink the set of the deduced type arguments for prmtype
+ * by adjusting top-qualifier of the argtype.
+ *
+ * prmtype argtype ta
+ * T <- const(E)[] const(E)[]
+ * T <- const(E[]) const(E)[]
+ * qualifier(T) <- const(E)[] const(E[])
+ * qualifier(T) <- const(E[]) const(E[])
+ */
+ Type ta = argtype.castMod(prmtype.mod ? argtype.nextOf().mod : 0);
+ if (ta != argtype)
+ {
+ Expression ea = farg.copy();
+ ea.type = ta;
+ oarg = ea;
+ }
+ }
+
+ if (fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams && argi + 1 < nfargs)
+ goto Lvarargs;
+
+ uint 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 == MATCH.nomatch && prmtype.deco)
+ m = farg.implicitConvTo(prmtype);
+
+ if (m == MATCH.nomatch)
+ {
+ AggregateDeclaration ad = isAggregate(farg.type);
+ if (ad && ad.aliasthis && !isRecursiveAliasThis(att, argtype))
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12537
+ // The isRecursiveAliasThis() call above
+
+ /* If a semantic error occurs while doing alias this,
+ * eg purity(https://issues.dlang.org/show_bug.cgi?id=7295),
+ * just regard it as not a match.
+ */
+ if (auto e = resolveAliasThis(sc, farg, true))
+ {
+ farg = e;
+ goto Lretry;
+ }
+ }
+ }
+
+ if (m > MATCH.nomatch && (fparam.storageClass & (STC.ref_ | STC.auto_)) == STC.ref_)
+ {
+ if (!farg.isLvalue())
+ {
+ if ((farg.op == TOK.string_ || farg.op == TOK.slice) && (prmtype.ty == Tsarray || prmtype.ty == Taarray))
+ {
+ // Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
+ }
+ else if (global.params.rvalueRefParam)
+ {
+ // Allow implicit conversion to ref
+ }
+ else
+ return nomatch();
+ }
+ }
+ if (m > MATCH.nomatch && (fparam.storageClass & STC.out_))
+ {
+ if (!farg.isLvalue())
+ return nomatch();
+ if (!farg.type.isMutable()) // https://issues.dlang.org/show_bug.cgi?id=11916
+ return nomatch();
+ }
+ if (m == MATCH.nomatch && (fparam.storageClass & STC.lazy_) && prmtype.ty == Tvoid && farg.type.ty != Tvoid)
+ m = MATCH.convert;
+ if (m != MATCH.nomatch)
+ {
+ if (m < match)
+ match = m; // pick worst match
+ argi++;
+ continue;
+ }
+ }
+
+ Lvarargs:
+ /* The following code for variadic arguments closely
+ * matches TypeFunction.callMatch()
+ */
+ if (!(fparameters.varargs == VarArg.typesafe && parami + 1 == nfparams))
+ return nomatch();
+
+ /* 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 = cast(TypeSArray)tb;
+ dinteger_t sz = tsa.dim.toInteger();
+ if (sz != nfargs - argi)
+ return nomatch();
+ }
+ else if (tb.ty == Taarray)
+ {
+ TypeAArray taa = cast(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;
+
+ uint 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)
+ return nomatch();
+
+ e = e.ctfeInterpret();
+ e = e.implicitCastTo(sco, Type.tsize_t);
+ e = e.optimize(WANTvalue);
+ if (!dim.equals(e))
+ return nomatch();
+ }
+ else
+ {
+ // This code matches code in TypeInstance.deduceType()
+ TemplateParameter tprm = (*parameters)[i];
+ TemplateValueParameter tvp = tprm.isTemplateValueParameter();
+ if (!tvp)
+ return nomatch();
+ Expression e = cast(Expression)(*dedtypes)[i];
+ if (e)
+ {
+ if (!dim.equals(e))
+ return nomatch();
+ }
+ else
+ {
+ Type vt = tvp.valType.typeSemantic(Loc.initial, sc);
+ MATCH m = dim.implicitConvTo(vt);
+ if (m == MATCH.nomatch)
+ return nomatch();
+ (*dedtypes)[i] = dim;
+ }
+ }
+ }
+ goto case Tarray;
+ }
+ case Tarray:
+ {
+ TypeArray ta = cast(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 = MATCH.exact;
+ }
+ else
+ {
+ m = arg.implicitConvTo(tret);
+ if (m == MATCH.nomatch)
+ {
+ if (tret.toBasetype().ty == Tvoid)
+ m = MATCH.convert;
+ }
+ }
+ }
+ else
+ {
+ uint wm = 0;
+ m = deduceType(arg, paramscope, ta.next, parameters, dedtypes, &wm, inferStart);
+ wildmatch |= wm;
+ }
+ if (m == MATCH.nomatch)
+ return nomatch();
+ if (m < match)
+ match = m;
+ }
+ goto Lmatch;
+ }
+ case Tclass:
+ case Tident:
+ goto Lmatch;
+
+ default:
+ return nomatch();
+ }
+ assert(0);
+ }
+ //printf(". argi = %d, nfargs = %d, nfargs2 = %d\n", argi, nfargs, nfargs2);
+ if (argi != nfargs2 && fparameters.varargs == VarArg.none)
+ return nomatch();
+ }
+
+ Lmatch:
+ foreach (ref dedtype; *dedtypes)
+ {
+ Type at = isType(dedtype);
+ if (at)
+ {
+ if (at.ty == Tnone)
+ {
+ TypeDeduced xt = cast(TypeDeduced)at;
+ at = xt.tded; // 'unbox'
+ }
+ dedtype = at.merge2();
+ }
+ }
+ for (size_t i = ntargs; i < dedargs.dim; i++)
+ {
+ TemplateParameter tparam = (*parameters)[i];
+ //printf("tparam[%d] = %s\n", i, tparam.ident.toChars());
+
+ /* For T:T*, the dedargs is the T*, dedtypes is the T
+ * But for function templates, we really need them to match
+ */
+ RootObject oarg = (*dedargs)[i];
+ RootObject oded = (*dedtypes)[i];
+ //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded);
+ //if (oarg) printf("oarg: %s\n", oarg.toChars());
+ //if (oded) printf("oded: %s\n", oded.toChars());
+ if (oarg)
+ continue;
+
+ 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 == MATCH.nomatch)
+ return nomatch();
+ 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
+ {
+ // Discussion: https://issues.dlang.org/show_bug.cgi?id=16484
+ if (MATCH.convert < matchTiargs)
+ matchTiargs = MATCH.convert;
+ }
+ }
+ 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.dim - 1)
+ {
+ // make tuple argument an empty tuple
+ oded = new Tuple();
+ }
+ else
+ return nomatch();
+ }
+ if (isError(oded))
+ return matcherror();
+ 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 == MATCH.nomatch)
+ return nomatch();
+ 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;
+ }
+
+ /* https://issues.dlang.org/show_bug.cgi?id=7469
+ * As same as the code for 7469 in findBestMatch,
+ * expand a Tuple in dedargs to normalize template arguments.
+ */
+ if (auto d = dedargs.dim)
+ {
+ if (auto va = isTuple((*dedargs)[d - 1]))
+ {
+ 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(paramscope.scopesym);
+ Scope* sc2 = _scope;
+ sc2 = sc2.push(paramscope.scopesym);
+ sc2 = sc2.push(ti);
+ sc2.parent = ti;
+ sc2.tinst = ti;
+ sc2.minst = sc.minst;
+ sc2.stc |= fd.storage_class & STC.deprecated_;
+
+ fd = doHeaderInstantiation(ti, sc2, fd, tthis, fargs);
+
+ sc2 = sc2.pop();
+ sc2 = sc2.pop();
+
+ if (!fd)
+ return nomatch();
+ }
+
+ if (constraint)
+ {
+ if (!evaluateConstraint(ti, sc, paramscope, dedargs, fd))
+ return nomatch();
+ }
+
+ version (none)
+ {
+ for (size_t i = 0; i < dedargs.dim; i++)
+ {
+ RootObject o = (*dedargs)[i];
+ printf("\tdedargs[%d] = %d, %s\n", i, o.dyncast(), o.toChars());
+ }
+ }
+
+ paramscope.pop();
+ //printf("\tmatch %d\n", match);
+ return MATCHpair(matchTiargs, match);
+ }
+
+ /**************************************************
+ * Declare template parameter tp with value o, and install it in the scope sc.
+ */
+ RootObject 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 == TOK.type)
+ ta = ea.type;
+ else if (ea && ea.op == TOK.scope_)
+ sa = (cast(ScopeExp)ea).sds;
+ else if (ea && (ea.op == TOK.this_ || ea.op == TOK.super_))
+ sa = (cast(ThisExp)ea).var;
+ else if (ea && ea.op == TOK.function_)
+ {
+ if ((cast(FuncExp)ea).td)
+ sa = (cast(FuncExp)ea).td;
+ else
+ sa = (cast(FuncExp)ea).fd;
+ }
+
+ if (ta)
+ {
+ //printf("type %s\n", ta.toChars());
+ auto ad = new AliasDeclaration(Loc.initial, tp.ident, ta);
+ ad.storage_class |= STC.templateparameter;
+ d = ad;
+ }
+ else if (sa)
+ {
+ //printf("Alias %s %s;\n", sa.ident.toChars(), tp.ident.toChars());
+ auto ad = new AliasDeclaration(Loc.initial, tp.ident, sa);
+ ad.storage_class |= STC.templateparameter;
+ d = ad;
+ }
+ 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 = STC.manifest | STC.templateparameter;
+ d = v;
+ }
+ else if (va)
+ {
+ //printf("\ttuple\n");
+ d = new TupleDeclaration(loc, tp.ident, &va.objects);
+ }
+ else
+ {
+ assert(0);
+ }
+ d.storage_class |= STC.templateparameter;
+
+ if (ta)
+ {
+ Type t = ta;
+ // consistent with Type.checkDeprecated()
+ while (t.ty != Tenum)
+ {
+ if (!t.nextOf())
+ break;
+ t = (cast(TypeNext)t).next;
+ }
+ if (Dsymbol s = t.toDsymbol(sc))
+ {
+ if (s.isDeprecated())
+ d.storage_class |= STC.deprecated_;
+ }
+ }
+ else if (sa)
+ {
+ if (sa.isDeprecated())
+ d.storage_class |= STC.deprecated_;
+ }
+
+ if (!sc.insert(d))
+ error("declaration `%s` is already defined", tp.ident.toChars());
+ d.dsymbolSemantic(sc);
+ /* So the caller's o gets updated with the result of semantic() being run on o
+ */
+ if (v)
+ o = v._init.initializerToExpression();
+ return o;
+ }
+
+ /*************************************************
+ * Limited function template instantiation for using fd.leastAsSpecialized()
+ */
+ extern (D) FuncDeclaration doHeaderInstantiation(TemplateInstance ti, Scope* sc2, FuncDeclaration fd, Type tthis, Expressions* fargs)
+ {
+ assert(fd);
+ version (none)
+ {
+ printf("doHeaderInstantiation this = %s\n", toChars());
+ }
+
+ // 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);
+ auto tf = fd.type.isTypeFunction();
+ tf.fargs = fargs;
+
+ if (tthis)
+ {
+ // Match 'tthis' to any TemplateThisParameter's
+ bool hasttp = false;
+ foreach (tp; *parameters)
+ {
+ TemplateThisParameter ttp = tp.isTemplateThisParameter();
+ if (ttp)
+ hasttp = true;
+ }
+ if (hasttp)
+ {
+ tf = cast(TypeFunction)tf.addSTC(ModToStc(tthis.mod));
+ assert(!tf.deco);
+ }
+ }
+
+ Scope* scx = sc2.push();
+
+ // Shouldn't run semantic on default arguments and return type.
+ foreach (ref params; *tf.parameterList.parameters)
+ params.defaultArg = null;
+ tf.incomplete = true;
+
+ if (fd.isCtorDeclaration())
+ {
+ // For constructors, emitting return type is necessary for
+ // isReturnIsolated() in functionResolve.
+ tf.isctor = true;
+
+ Dsymbol parent = toParentDecl();
+ Type tret;
+ AggregateDeclaration ad = parent.isAggregateDeclaration();
+ if (!ad || parent.isUnionDeclaration())
+ {
+ tret = Type.tvoid;
+ }
+ else
+ {
+ tret = ad.handleType();
+ assert(tret);
+ tret = tret.addStorageClass(fd.storage_class | scx.stc);
+ tret = tret.addMod(tf.mod);
+ }
+ tf.next = tret;
+ if (ad && ad.isStructDeclaration())
+ tf.isref = 1;
+ //printf("tf = %s\n", tf.toChars());
+ }
+ else
+ tf.next = null;
+ fd.type = tf;
+ fd.type = fd.type.addSTC(scx.stc);
+ fd.type = fd.type.typeSemantic(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;
+ }
+
+ debug (FindExistingInstance)
+ {
+ __gshared uint nFound, nNotFound, nAdded, nRemoved;
+
+ shared static ~this()
+ {
+ printf("debug (FindExistingInstance) nFound %u, nNotFound: %u, nAdded: %u, nRemoved: %u\n",
+ nFound, nNotFound, nAdded, nRemoved);
+ }
+ }
+
+ /****************************************************
+ * Given a new instance tithis of this TemplateDeclaration,
+ * see if there already exists an instance.
+ * If so, return that existing instance.
+ */
+ extern (D) TemplateInstance findExistingInstance(TemplateInstance tithis, Expressions* fargs)
+ {
+ //printf("findExistingInstance() %s\n", tithis.toChars());
+ tithis.fargs = fargs;
+ auto tibox = TemplateInstanceBox(tithis);
+ auto p = tibox in instances;
+ debug (FindExistingInstance) ++(p ? nFound : nNotFound);
+ //if (p) printf("\tfound %p\n", *p); else printf("\tnot found\n");
+ return p ? *p : null;
+ }
+
+ /********************************************
+ * Add instance ti to TemplateDeclaration's table of instances.
+ * Return a handle we can use to later remove it if it fails instantiation.
+ */
+ extern (D) TemplateInstance addInstance(TemplateInstance ti)
+ {
+ //printf("addInstance() %p %s\n", instances, ti.toChars());
+ auto tibox = TemplateInstanceBox(ti);
+ instances[tibox] = ti;
+ debug (FindExistingInstance) ++nAdded;
+ return ti;
+ }
+
+ /*******************************************
+ * Remove TemplateInstance from table of instances.
+ * Input:
+ * handle returned by addInstance()
+ */
+ extern (D) void removeInstance(TemplateInstance ti)
+ {
+ //printf("removeInstance() %s\n", ti.toChars());
+ auto tibox = TemplateInstanceBox(ti);
+ debug (FindExistingInstance) ++nRemoved;
+ instances.remove(tibox);
+ }
+
+ override inout(TemplateDeclaration) isTemplateDeclaration() inout
+ {
+ return this;
+ }
+
+ /**
+ * Check if the last template parameter is a tuple one,
+ * and returns it if so, else returns `null`.
+ *
+ * Returns:
+ * The last template parameter if it's a `TemplateTupleParameter`
+ */
+ TemplateTupleParameter isVariadic()
+ {
+ size_t dim = parameters.dim;
+ if (dim == 0)
+ return null;
+ return (*parameters)[dim - 1].isTemplateTupleParameter();
+ }
+
+ extern(C++) override bool isDeprecated() const
+ {
+ return this.deprecated_;
+ }
+
+ /***********************************
+ * We can overload templates.
+ */
+ override bool isOverloadable() const
+ {
+ return true;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+extern (C++) final class TypeDeduced : Type
+{
+ Type tded;
+ Expressions argexps; // corresponding expressions
+ Types tparams; // tparams[i].mod
+
+ extern (D) this(Type tt, Expression e, Type tparam)
+ {
+ super(Tnone);
+ tded = tt;
+ argexps.push(e);
+ tparams.push(tparam);
+ }
+
+ 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 = MATCH.exact;
+ foreach (j, e; argexps)
+ {
+ assert(e);
+ if (e == emptyArrayElement)
+ continue;
+
+ Type t = tt.addMod(tparams[j].mod).substWildTo(MODFlags.const_);
+
+ MATCH m = e.implicitConvTo(t);
+ if (match > m)
+ match = m;
+ if (match == MATCH.nomatch)
+ break;
+ }
+ return match;
+ }
+}
+
+
+/*************************************************
+ * 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(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Objects* tiargs,
+ Type tthis, Expressions* fargs, const(char)** pMessage = null)
+{
+ Expression[] fargs_ = fargs.peekSlice();
+ version (none)
+ {
+ printf("functionResolve() dstart = %s\n", dstart.toChars());
+ printf(" tiargs:\n");
+ if (tiargs)
+ {
+ for (size_t i = 0; i < tiargs.dim; i++)
+ {
+ RootObject arg = (*tiargs)[i];
+ printf("\t%s\n", arg.toChars());
+ }
+ }
+ printf(" fargs:\n");
+ for (size_t i = 0; i < (fargs ? fargs.dim : 0); i++)
+ {
+ Expression arg = (*fargs)[i];
+ printf("\t%s %s\n", arg.type.toChars(), arg.toChars());
+ //printf("\tty = %d\n", arg.type.ty);
+ }
+ //printf("stc = %llx\n", dstart.scope.stc);
+ //printf("match:t/f = %d/%d\n", ta_last, m.last);
+ }
+
+ // results
+ int property = 0; // 0: uninitialized
+ // 1: seen @property
+ // 2: not @property
+ size_t ov_index = 0;
+ TemplateDeclaration td_best;
+ TemplateInstance ti_best;
+ MATCH ta_last = m.last != MATCH.nomatch ? MATCH.exact : MATCH.nomatch;
+ Type tthis_best;
+
+ int applyFunction(FuncDeclaration fd)
+ {
+ // skip duplicates
+ if (fd == m.lastf)
+ return 0;
+ // explicitly specified tiargs never match to non template function
+ if (tiargs && tiargs.dim > 0)
+ return 0;
+
+ // constructors need a valid scope in order to detect semantic errors
+ if (!fd.isCtorDeclaration &&
+ fd.semanticRun < PASS.semanticdone)
+ {
+ Ungag ungag = fd.ungagSpeculative();
+ fd.dsymbolSemantic(null);
+ }
+ if (fd.semanticRun < PASS.semanticdone)
+ {
+ .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());
+ auto tf = cast(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.isReturnIsolated());
+ if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
+ tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
+ fd.isReturnIsolated())
+ {
+ /* && tf.isShared() == tthis_fd.isShared()*/
+ // Uniquely constructed object can ignore shared qualifier.
+ // TODO: Is this appropriate?
+ tthis_fd = null;
+ }
+ else
+ return 0; // MATCH.nomatch
+ }
+ /* Fix Issue 17970:
+ If a struct is declared as shared the dtor is automatically
+ considered to be shared, but when the struct is instantiated
+ the instance is no longer considered to be shared when the
+ function call matching is done. The fix makes it so that if a
+ struct declaration is shared, when the destructor is called,
+ the instantiated struct is also considered shared.
+ */
+ if (auto dt = fd.isDtorDeclaration())
+ {
+ auto dtmod = dt.type.toTypeFunction();
+ auto shared_dtor = dtmod.mod & MODFlags.shared_;
+ auto shared_this = tthis_fd !is null ?
+ tthis_fd.mod & MODFlags.shared_ : 0;
+ if (shared_dtor && !shared_this)
+ tthis_fd = dtmod;
+ else if (shared_this && !shared_dtor && tthis_fd !is null)
+ tf.mod = tthis_fd.mod;
+ }
+ MATCH mfa = tf.callMatch(tthis_fd, fargs_, 0, pMessage, sc);
+ //printf("test1: mfa = %d\n", mfa);
+ if (mfa == MATCH.nomatch)
+ return 0;
+
+ 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));
+ const lastCovariant = m.lastf.type.covariant(fd.type);
+ const firstCovariant = fd.type.covariant(m.lastf.type);
+
+ if (lastCovariant == Covariant.yes || lastCovariant == Covariant.no)
+ {
+ if (firstCovariant != Covariant.yes && firstCovariant != Covariant.no)
+ {
+ goto LlastIsBetter;
+ }
+ }
+ else if (firstCovariant == Covariant.yes || firstCovariant == Covariant.no)
+ {
+ 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 none has a body then don't care because the same
+ * real function would be linked to the decl (e.g from object file)
+ */
+ if (tf.equals(m.lastf.type) &&
+ fd.storage_class == m.lastf.storage_class &&
+ fd.parent == m.lastf.parent &&
+ fd.visibility == m.lastf.visibility &&
+ fd.linkage == m.lastf.linkage)
+ {
+ if (fd.fbody && !m.lastf.fbody)
+ goto LfIsBetter;
+ if (!fd.fbody)
+ goto LlastIsBetter;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=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 = MATCH.exact;
+ m.last = mfa;
+ m.lastf = fd;
+ tthis_best = tthis_fd;
+ ov_index = 0;
+ m.count = 1;
+ 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 == PASS.init && td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ Ungag ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.init)
+ {
+ .error(loc, "forward reference to template `%s`", td.toChars());
+ Lerror:
+ m.lastf = null;
+ m.count = 0;
+ m.last = MATCH.nomatch;
+ return 1;
+ }
+ //printf("td = %s\n", td.toChars());
+
+ auto f = td.onemember ? td.onemember.isFuncDeclaration() : null;
+ if (!f)
+ {
+ if (!tiargs)
+ tiargs = new Objects();
+ auto ti = new TemplateInstance(loc, td, tiargs);
+ Objects dedtypes = Objects(td.parameters.dim);
+ assert(td.semanticRun != PASS.init);
+ MATCH mta = td.matchWithInstance(sc, ti, &dedtypes, fargs, 0);
+ //printf("matchWithInstance = %d\n", mta);
+ if (mta == MATCH.nomatch || mta < ta_last) // no match or less match
+ return 0;
+
+ ti.templateInstanceSemantic(sc, fargs);
+ if (!ti.inst) // if template failed to expand
+ return 0;
+
+ Dsymbol s = ti.inst.toAlias();
+ FuncDeclaration fd;
+ if (auto tdx = s.isTemplateDeclaration())
+ {
+ Objects dedtypesX; // empty tiargs
+
+ // https://issues.dlang.org/show_bug.cgi?id=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, FuncResolveFlag.quiet);
+
+ tdx.previous = pr.prev; // unlink from threaded list
+ }
+ else if (s.isFuncDeclaration())
+ {
+ fd = resolveFuncCall(loc, sc, s, null, tthis, fargs, FuncResolveFlag.quiet);
+ }
+ else
+ goto Lerror;
+
+ if (!fd)
+ return 0;
+
+ if (fd.type.ty != Tfunction)
+ {
+ m.lastf = fd; // to propagate "error match"
+ m.count = 1;
+ m.last = MATCH.nomatch;
+ return 1;
+ }
+
+ Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null;
+
+ auto tf = cast(TypeFunction)fd.type;
+ MATCH mfa = tf.callMatch(tthis_fd, fargs_, 0, null, sc);
+ 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;
+
+ // td_best and td are ambiguous
+ //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.
+ */
+ auto ti = new TemplateInstance(loc, td, tiargs);
+ ti.parent = td.parent; // Maybe calculating valid 'enclosing' is unnecessary.
+
+ auto fd = f;
+ MATCHpair x = td.deduceFunctionTemplateMatch(ti, sc, fd, tthis, fargs);
+ MATCH mta = x.mta;
+ MATCH mfa = x.mfa;
+ //printf("match:t/f = %d/%d\n", mta, mfa);
+ if (!fd || mfa == MATCH.nomatch)
+ continue;
+
+ Type tthis_fd = fd.needThis() ? tthis : null;
+
+ bool isCtorCall = tthis_fd && fd.isCtorDeclaration();
+ if (isCtorCall)
+ {
+ // Constructor call requires additional check.
+
+ auto tf = cast(TypeFunction)fd.type;
+ assert(tf.next);
+ if (MODimplicitConv(tf.mod, tthis_fd.mod) ||
+ tf.isWild() && tf.isShared() == tthis_fd.isShared() ||
+ fd.isReturnIsolated())
+ {
+ tthis_fd = null;
+ }
+ else
+ continue; // MATCH.nomatch
+ }
+
+ 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
+ auto tf1 = fd.type.isTypeFunction();
+ auto tf2 = m.lastf.type.isTypeFunction();
+ MATCH c1 = tf1.callMatch(tthis_fd, fargs_, 0, null, sc);
+ MATCH c2 = tf2.callMatch(tthis_best, fargs_, 0, null, sc);
+ //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;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=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;
+ }
+
+ auto td = dstart.isTemplateDeclaration();
+ if (td && td.funcroot)
+ dstart = td.funcroot;
+ overloadApply(dstart, (Dsymbol s)
+ {
+ if (s.errors)
+ return 0;
+ if (auto fd = s.isFuncDeclaration())
+ return applyFunction(fd);
+ if (auto td = s.isTemplateDeclaration())
+ return applyTemplate(td);
+ return 0;
+ }, sc);
+
+ //printf("td_best = %p, m.lastf = %p\n", td_best, m.lastf);
+ if (td_best && ti_best && m.count == 1)
+ {
+ // Matches to template function
+ assert(td_best.onemember && td_best.onemember.isFuncDeclaration());
+ /* The best match is td_best with arguments tdargs.
+ * Now instantiate the template.
+ */
+ assert(td_best._scope);
+ if (!sc)
+ sc = td_best._scope; // workaround for Type.aliasthisOf
+
+ auto ti = new TemplateInstance(loc, td_best, ti_best.tiargs);
+ ti.templateInstanceSemantic(sc, fargs);
+
+ m.lastf = ti.toAlias().isFuncDeclaration();
+ if (!m.lastf)
+ goto Lnomatch;
+ if (ti.errors)
+ {
+ Lerror:
+ m.count = 1;
+ assert(m.lastf);
+ m.last = MATCH.nomatch;
+ return;
+ }
+
+ // look forward instantiated overload function
+ // Dsymbol.oneMembers is alredy called in TemplateInstance.semantic.
+ // it has filled overnext0d
+ while (ov_index--)
+ {
+ m.lastf = m.lastf.overnext0;
+ assert(m.lastf);
+ }
+
+ tthis_best = m.lastf.needThis() && !m.lastf.isCtorDeclaration() ? tthis : null;
+
+ if (m.lastf.type.ty == Terror)
+ goto Lerror;
+ auto tf = m.lastf.type.isTypeFunction();
+ if (!tf.callMatch(tthis_best, fargs_, 0, null, sc))
+ goto Lnomatch;
+
+ /* As https://issues.dlang.org/show_bug.cgi?id=3682 shows,
+ * a template instance can be matched while instantiating
+ * that same template. Thus, the function type can be incomplete. Complete it.
+ *
+ * https://issues.dlang.org/show_bug.cgi?id=9208
+ * For auto function, completion should be deferred to the end of
+ * its semantic3. Should not complete it in here.
+ */
+ if (tf.next && !m.lastf.inferRetType)
+ {
+ m.lastf.type = tf.typeSemantic(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 = MATCH.nomatch;
+ }
+}
+
+/* ======================== Type ============================================ */
+
+/****
+ * Given an identifier, figure out which TemplateParameter it is.
+ * Return IDX_NOTFOUND if not found.
+ */
+private size_t templateIdentifierLookup(Identifier id, TemplateParameters* parameters)
+{
+ for (size_t i = 0; i < parameters.dim; i++)
+ {
+ TemplateParameter tp = (*parameters)[i];
+ if (tp.ident.equals(id))
+ return i;
+ }
+ return IDX_NOTFOUND;
+}
+
+private size_t templateParameterLookup(Type tparam, TemplateParameters* parameters)
+{
+ if (tparam.ty == Tident)
+ {
+ TypeIdentifier tident = cast(TypeIdentifier)tparam;
+ //printf("\ttident = '%s'\n", tident.toChars());
+ return templateIdentifierLookup(tident.ident, parameters);
+ }
+ return IDX_NOTFOUND;
+}
+
+private ubyte deduceWildHelper(Type t, Type* at, Type tparam)
+{
+ if ((tparam.mod & MODFlags.wild) == 0)
+ return 0;
+
+ *at = null;
+
+ auto X(T, U)(T U, U T)
+ {
+ return (U << 4) | T;
+ }
+
+ switch (X(tparam.mod, t.mod))
+ {
+ case X(MODFlags.wild, 0):
+ case X(MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.wildconst, 0):
+ case X(MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
+ {
+ ubyte wm = (t.mod & ~MODFlags.shared_);
+ if (wm == 0)
+ wm = MODFlags.mutable;
+ ubyte m = (t.mod & (MODFlags.const_ | MODFlags.immutable_)) | (tparam.mod & t.mod & MODFlags.shared_);
+ *at = t.unqualify(m);
+ return wm;
+ }
+ case X(MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ {
+ *at = t.unqualify(tparam.mod & t.mod);
+ return MODFlags.wild;
+ }
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Returns the common type of the 2 types.
+ */
+private Type rawTypeMerge(Type t1, Type t2)
+{
+ if (t1.equals(t2))
+ return t1;
+ if (t1.equivalent(t2))
+ return t1.castMod(MODmerge(t1.mod, t2.mod));
+
+ auto t1b = t1.toBasetype();
+ auto t2b = t2.toBasetype();
+ if (t1b.equals(t2b))
+ return t1b;
+ if (t1b.equivalent(t2b))
+ return t1b.castMod(MODmerge(t1b.mod, t2b.mod));
+
+ auto ty = implicitConvCommonTy(t1b.ty, t2b.ty);
+ if (ty != Terror)
+ return Type.basic[ty];
+
+ return null;
+}
+
+private MATCH deduceTypeHelper(Type t, Type* at, Type tparam)
+{
+ // 9*9 == 81 cases
+
+ auto X(T, U)(T U, U T)
+ {
+ return (U << 4) | T;
+ }
+
+ switch (X(tparam.mod, t.mod))
+ {
+ case X(0, 0):
+ case X(0, MODFlags.const_):
+ case X(0, MODFlags.wild):
+ case X(0, MODFlags.wildconst):
+ case X(0, MODFlags.shared_):
+ case X(0, MODFlags.shared_ | MODFlags.const_):
+ case X(0, MODFlags.shared_ | MODFlags.wild):
+ case X(0, MODFlags.shared_ | MODFlags.wildconst):
+ case X(0, MODFlags.immutable_):
+ // 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 MATCH.exact;
+ }
+ case X(MODFlags.const_, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.shared_, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.immutable_, MODFlags.immutable_):
+ // 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 MATCH.exact;
+ }
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wildconst):
+ // foo(const(U)) shared(const(T)) => shared(T)
+ // foo(inout(U)) shared(inout(T)) => shared(T)
+ // foo(inout(const(U))) shared(inout(const(T))) => shared(T)
+ {
+ *at = t.mutableOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.const_, 0):
+ case X(MODFlags.const_, MODFlags.wild):
+ case X(MODFlags.const_, MODFlags.wildconst):
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.const_, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.immutable_):
+ // foo(const(U)) T => T
+ // foo(const(U)) inout(T) => T
+ // foo(const(U)) inout(const(T)) => 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(shared(const(U))) immutable(T) => T
+ {
+ *at = t.mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.const_, MODFlags.shared_):
+ // foo(const(U)) shared(T) => shared(T)
+ {
+ *at = t;
+ return MATCH.constant;
+ }
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_, MODFlags.shared_ | MODFlags.wildconst):
+ // 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))
+ {
+ *at = t.unSharedOf();
+ return MATCH.exact;
+ }
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_):
+ // foo(shared(const(U))) shared(T) => T
+ {
+ *at = t.unSharedOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ // 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 MATCH.constant;
+ }
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.shared_ | MODFlags.wild):
+ // foo(shared(const(U))) shared(inout(T)) => T
+ {
+ *at = t.unSharedOf().mutableOf();
+ return MATCH.constant;
+ }
+ case X(MODFlags.wild, 0):
+ case X(MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.wildconst, 0):
+ case X(MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.wildconst, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.shared_, 0):
+ case X(MODFlags.shared_, MODFlags.const_):
+ case X(MODFlags.shared_, MODFlags.wild):
+ case X(MODFlags.shared_, MODFlags.wildconst):
+ case X(MODFlags.shared_, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.const_, 0):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.const_, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, 0):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.immutable_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wild, MODFlags.shared_ | MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, 0):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.const_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wild):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.wildconst):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_):
+ case X(MODFlags.shared_ | MODFlags.wildconst, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.immutable_, 0):
+ case X(MODFlags.immutable_, MODFlags.const_):
+ case X(MODFlags.immutable_, MODFlags.wild):
+ case X(MODFlags.immutable_, MODFlags.wildconst):
+ case X(MODFlags.immutable_, MODFlags.shared_):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.const_):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wild):
+ case X(MODFlags.immutable_, MODFlags.shared_ | MODFlags.wildconst):
+ // 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 MATCH.nomatch;
+
+ default:
+ assert(0);
+ }
+}
+
+__gshared Expression emptyArrayElement = null;
+
+/* 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, uint* wm = null, size_t inferStart = 0, bool ignoreAliasThis = false)
+{
+ extern (C++) final class DeduceType : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ Scope* sc;
+ Type tparam;
+ TemplateParameters* parameters;
+ Objects* dedtypes;
+ uint* wm;
+ size_t inferStart;
+ bool ignoreAliasThis;
+ MATCH result;
+
+ extern (D) this(Scope* sc, Type tparam, TemplateParameters* parameters, Objects* dedtypes, uint* wm, size_t inferStart, bool ignoreAliasThis)
+ {
+ this.sc = sc;
+ this.tparam = tparam;
+ this.parameters = parameters;
+ this.dedtypes = dedtypes;
+ this.wm = wm;
+ this.inferStart = inferStart;
+ this.ignoreAliasThis = ignoreAliasThis;
+ result = MATCH.nomatch;
+ }
+
+ override void visit(Type t)
+ {
+ if (!tparam)
+ goto Lnomatch;
+
+ if (t == tparam)
+ goto Lexact;
+
+ if (tparam.ty == Tident)
+ {
+ // Determine which parameter tparam is
+ size_t i = templateParameterLookup(tparam, parameters);
+ if (i == IDX_NOTFOUND)
+ {
+ if (!sc)
+ goto Lnomatch;
+
+ /* Need a loc to go with the semantic routine.
+ */
+ Loc loc;
+ if (parameters.dim)
+ {
+ TemplateParameter tp = (*parameters)[0];
+ loc = tp.loc;
+ }
+
+ /* BUG: what if tparam is a template instance, that
+ * has as an argument another Tident?
+ */
+ tparam = tparam.typeSemantic(loc, sc);
+ assert(tparam.ty != Tident);
+ result = deduceType(t, sc, tparam, parameters, dedtypes, wm);
+ return;
+ }
+
+ TemplateParameter tp = (*parameters)[i];
+
+ TypeIdentifier tident = cast(TypeIdentifier)tparam;
+ if (tident.idents.dim > 0)
+ {
+ //printf("matching %s to %s\n", tparam.toChars(), t.toChars());
+ Dsymbol s = t.toDsymbol(sc);
+ for (size_t j = tident.idents.dim; j-- > 0;)
+ {
+ RootObject id = tident.idents[j];
+ if (id.dyncast() == DYNCAST.identifier)
+ {
+ if (!s || !s.parent)
+ goto Lnomatch;
+ Dsymbol s2 = s.parent.search(Loc.initial, cast(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 = cast(Type)(*dedtypes)[i];
+ if (at && at.ty == Tnone)
+ at = (cast(TypeDeduced)at).tded;
+ if (!at || tt.equals(at))
+ {
+ (*dedtypes)[i] = tt;
+ goto Lexact;
+ }
+ }
+ if (tp.isTemplateAliasParameter())
+ {
+ Dsymbol s2 = cast(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 = cast(Type)(*dedtypes)[i];
+ Type tt;
+ if (ubyte wx = wm ? deduceWildHelper(t, &tt, tparam) : 0)
+ {
+ // type vs (none)
+ if (!at)
+ {
+ (*dedtypes)[i] = tt;
+ *wm |= wx;
+ result = MATCH.constant;
+ return;
+ }
+
+ // type vs expressions
+ if (at.ty == Tnone)
+ {
+ TypeDeduced xt = cast(TypeDeduced)at;
+ result = xt.matchAll(tt);
+ if (result > MATCH.nomatch)
+ {
+ (*dedtypes)[i] = tt;
+ if (result > MATCH.constant)
+ result = MATCH.constant; // limit level for inout matches
+ }
+ 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 |= MODFlags.const_;
+ goto Lconst;
+ }
+ if (at.implicitConvTo(tt.constOf()))
+ {
+ (*dedtypes)[i] = tt.constOf().mutableOf();
+ *wm |= MODFlags.const_;
+ 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 = cast(TypeDeduced)at;
+ result = xt.matchAll(tt);
+ if (result > MATCH.nomatch)
+ {
+ (*dedtypes)[i] = tt;
+ }
+ 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()) >= MATCH.constant)
+ {
+ goto Lexact;
+ }
+ }
+ goto Lnomatch;
+ }
+
+ if (tparam.ty == Ttypeof)
+ {
+ /* Need a loc to go with the semantic routine.
+ */
+ Loc loc;
+ if (parameters.dim)
+ {
+ TemplateParameter tp = (*parameters)[0];
+ loc = tp.loc;
+ }
+
+ tparam = tparam.typeSemantic(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 == MATCH.nomatch && !ignoreAliasThis)
+ {
+ if (t.ty == Tclass)
+ {
+ TypeClass tc = cast(TypeClass)t;
+ if (tc.sym.aliasthis && !(tc.att & AliasThisRec.tracingDT))
+ {
+ if (auto ato = t.aliasthisOf())
+ {
+ tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracingDT);
+ m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+ tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracingDT);
+ }
+ }
+ }
+ else if (t.ty == Tstruct)
+ {
+ TypeStruct ts = cast(TypeStruct)t;
+ if (ts.sym.aliasthis && !(ts.att & AliasThisRec.tracingDT))
+ {
+ if (auto ato = t.aliasthisOf())
+ {
+ ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracingDT);
+ m = deduceType(ato, sc, tparam, parameters, dedtypes, wm);
+ ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracingDT);
+ }
+ }
+ }
+ }
+ 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())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=12403
+ // In IFTI, stop inout matching on transitive part of AA types.
+ tpn = tpn.substWildTo(MODFlags.mutable);
+ }
+
+ result = deduceType(t.nextOf(), sc, tpn, parameters, dedtypes, wm);
+ return;
+ }
+
+ Lexact:
+ result = MATCH.exact;
+ return;
+
+ Lnomatch:
+ result = MATCH.nomatch;
+ return;
+
+ Lconst:
+ result = MATCH.constant;
+ }
+
+ override void visit(TypeVector t)
+ {
+ if (tparam.ty == Tvector)
+ {
+ TypeVector tp = cast(TypeVector)tparam;
+ result = deduceType(t.basetype, sc, tp.basetype, parameters, dedtypes, wm);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeDArray t)
+ {
+ visit(cast(Type)t);
+ }
+
+ override 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 >= MATCH.constant) ? MATCH.convert : MATCH.nomatch;
+ return;
+ }
+
+ TemplateParameter tp = null;
+ Expression edim = null;
+ size_t i;
+ if (tparam.ty == Tsarray)
+ {
+ TypeSArray tsa = cast(TypeSArray)tparam;
+ if (tsa.dim.op == TOK.variable && (cast(VarExp)tsa.dim).var.storage_class & STC.templateparameter)
+ {
+ Identifier id = (cast(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 = cast(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.initial, 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(cast(Type)t);
+ }
+
+ override void visit(TypeAArray t)
+ {
+ // Extra check that index type must match
+ if (tparam && tparam.ty == Taarray)
+ {
+ TypeAArray tp = cast(TypeAArray)tparam;
+ if (!deduceType(t.index, sc, tp.index, parameters, dedtypes))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeFunction t)
+ {
+ // Extra check that function characteristics must match
+ if (!tparam)
+ return visit(cast(Type)t);
+
+ if (auto tp = tparam.isTypeFunction())
+ {
+ if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ foreach (fparam; *tp.parameterList.parameters)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=2579
+ // Apply function parameter storage classes to parameter types
+ fparam.type = fparam.type.addStorageClass(fparam.storageClass);
+ fparam.storageClass &= ~(STC.TYPECTOR | STC.in_);
+
+ // https://issues.dlang.org/show_bug.cgi?id=15243
+ // Resolve parameter type if it's not related with template parameters
+ if (!reliesOnTemplateParameters(fparam.type, (*parameters)[inferStart .. parameters.dim]))
+ {
+ auto tx = fparam.type.typeSemantic(Loc.initial, sc);
+ if (tx.ty == Terror)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ fparam.type = tx;
+ }
+ }
+
+ size_t nfargs = t.parameterList.length;
+ size_t nfparams = tp.parameterList.length;
+
+ /* 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 = cast(TypeIdentifier)fparam.type;
+ if (tid.idents.dim)
+ goto L1;
+
+ /* Look through parameters to find tuple matching tid.ident
+ */
+ size_t tupi = 0;
+ for (; 1; tupi++)
+ {
+ if (tupi == parameters.dim)
+ goto L1;
+ TemplateParameter tx = (*parameters)[tupi];
+ TemplateTupleParameter tup = tx.isTemplateTupleParameter();
+ if (tup && tup.ident.equals(tid.ident))
+ break;
+ }
+
+ /* The types of the function arguments [nfparams - 1 .. nfargs]
+ * now form the tuple argument.
+ */
+ size_t tuple_dim = nfargs - (nfparams - 1);
+
+ /* See if existing tuple, and whether it matches or not
+ */
+ RootObject o = (*dedtypes)[tupi];
+ if (o)
+ {
+ // Existing deduced argument must be a tuple, and must match
+ Tuple tup = isTuple(o);
+ if (!tup || tup.objects.dim != tuple_dim)
+ {
+ result = MATCH.nomatch;
+ 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 = MATCH.nomatch;
+ return;
+ }
+ }
+ }
+ else
+ {
+ // Create new tuple
+ auto tup = new Tuple(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 = MATCH.nomatch;
+ return;
+ }
+ L2:
+ assert(nfparams <= tp.parameterList.length);
+ foreach (i, ap; tp.parameterList)
+ {
+ if (i == nfparams)
+ break;
+
+ Parameter a = t.parameterList[i];
+
+ if (!a.isCovariant(t.isref, ap) ||
+ !deduceType(a.type, sc, ap.type, parameters, dedtypes))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeIdentifier t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tident)
+ {
+ TypeIdentifier tp = cast(TypeIdentifier)tparam;
+ for (size_t i = 0; i < t.idents.dim; i++)
+ {
+ RootObject id1 = t.idents[i];
+ RootObject id2 = tp.idents[i];
+ if (!id1.equals(id2))
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+ }
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeInstance t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tinstance && t.tempinst.tempdecl)
+ {
+ TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ TypeInstance tp = cast(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.
+ * https://issues.dlang.org/show_bug.cgi?id=1454
+ */
+ auto 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)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=14290
+ // Try to match with ti.tempecl,
+ // only when ti is an enclosing instance.
+ Dsymbol p = sc.parent;
+ while (p && p != ti)
+ p = p.parent;
+ if (p)
+ s = ti.tempdecl;
+ }
+ }
+ if (s)
+ {
+ s = s.toAlias();
+ TemplateDeclaration td = s.isTemplateDeclaration();
+ if (td)
+ {
+ if (td.overroot)
+ td = td.overroot;
+ for (; td; td = td.overnext)
+ {
+ if (td == tempdecl)
+ goto L2;
+ }
+ }
+ }
+ goto Lnomatch;
+ }
+ TemplateParameter tpx = (*parameters)[i];
+ if (!tpx.matchArg(sc, tempdecl, i, parameters, dedtypes, null))
+ goto Lnomatch;
+ }
+ else if (tempdecl != tp.tempinst.tempdecl)
+ goto Lnomatch;
+
+ L2:
+ for (size_t i = 0; 1; i++)
+ {
+ //printf("\ttest: tempinst.tiargs[%d]\n", i);
+ RootObject o1 = null;
+ if (i < t.tempinst.tiargs.dim)
+ o1 = (*t.tempinst.tiargs)[i];
+ else if (i < t.tempinst.tdtypes.dim && i < tp.tempinst.tiargs.dim)
+ {
+ // Pick up default arg
+ o1 = t.tempinst.tdtypes[i];
+ }
+ else if (i >= tp.tempinst.tiargs.dim)
+ break;
+
+ if (i >= tp.tempinst.tiargs.dim)
+ {
+ size_t dim = tempdecl.parameters.dim - (tempdecl.isVariadic() ? 1 : 0);
+ while (i < dim && ((*tempdecl.parameters)[i].dependent || (*tempdecl.parameters)[i].hasDefaultArg()))
+ {
+ i++;
+ }
+ if (i >= dim)
+ break; // match if all remained parameters are dependent
+ goto Lnomatch;
+ }
+
+ RootObject o2 = (*tp.tempinst.tiargs)[i];
+ Type t2 = isType(o2);
+
+ size_t j = (t2 && t2.ty == Tident && i == tp.tempinst.tiargs.dim - 1)
+ ? templateParameterLookup(t2, parameters) : IDX_NOTFOUND;
+ if (j != IDX_NOTFOUND && j == parameters.dim - 1 &&
+ (*parameters)[j].isTemplateTupleParameter())
+ {
+ /* Given:
+ * struct A(B...) {}
+ * alias A!(int, float) X;
+ * static if (is(X Y == A!(Z), Z...)) {}
+ * deduce that Z is a tuple(int, float)
+ */
+
+ /* Create tuple from remaining args
+ */
+ size_t vtdim = (tempdecl.isVariadic() ? t.tempinst.tiargs.dim : t.tempinst.tdtypes.dim) - i;
+ auto vt = new Tuple(vtdim);
+ for (size_t k = 0; k < vtdim; k++)
+ {
+ RootObject o;
+ if (k < t.tempinst.tiargs.dim)
+ o = (*t.tempinst.tiargs)[i + k];
+ else // Pick up default arg
+ o = t.tempinst.tdtypes[i + k];
+ vt.objects[k] = o;
+ }
+
+ Tuple v = cast(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);
+ version (none)
+ {
+ Tuple v1 = isTuple(o1);
+ Tuple v2 = isTuple(o2);
+ if (t1)
+ printf("t1 = %s\n", t1.toChars());
+ if (t2)
+ printf("t2 = %s\n", t2.toChars());
+ if (e1)
+ printf("e1 = %s\n", e1.toChars());
+ if (e2)
+ printf("e2 = %s\n", e2.toChars());
+ if (s1)
+ printf("s1 = %s\n", s1.toChars());
+ if (s2)
+ printf("s2 = %s\n", s2.toChars());
+ if (v1)
+ printf("v1 = %s\n", v1.toChars());
+ if (v2)
+ printf("v2 = %s\n", v2.toChars());
+ }
+
+ 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 == TOK.variable && ((cast(VarExp)e2).var.storage_class & STC.templateparameter))
+ {
+ /*
+ * (T:Number!(e2), int e2)
+ */
+ j = templateIdentifierLookup((cast(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 = e2.expressionSemantic(sc); // https://issues.dlang.org/show_bug.cgi?id=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((cast(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((cast(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(cast(Type)t);
+ return;
+
+ Lnomatch:
+ //printf("no match\n");
+ result = MATCH.nomatch;
+ }
+
+ override 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)
+ {
+ auto tx = new TypeInstance(Loc.initial, ti);
+ result = deduceType(tx, sc, tparam, parameters, dedtypes, wm);
+ return;
+ }
+
+ /* Match things like:
+ * S!(T).foo
+ */
+ TypeInstance tpi = cast(TypeInstance)tparam;
+ if (tpi.idents.dim)
+ {
+ RootObject id = tpi.idents[tpi.idents.dim - 1];
+ if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
+ {
+ Type tparent = t.sym.parent.getType();
+ if (tparent)
+ {
+ /* Slice off the .foo in S!(T).foo
+ */
+ tpi.idents.dim--;
+ result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+ tpi.idents.dim++;
+ return;
+ }
+ }
+ }
+ }
+
+ // Extra check
+ if (tparam && tparam.ty == Tstruct)
+ {
+ TypeStruct tp = cast(TypeStruct)tparam;
+
+ //printf("\t%d\n", (MATCH) t.implicitConvTo(tp));
+ if (wm && t.deduceWild(tparam, false))
+ {
+ result = MATCH.constant;
+ return;
+ }
+ result = t.implicitConvTo(tp);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(TypeEnum t)
+ {
+ // Extra check
+ if (tparam && tparam.ty == Tenum)
+ {
+ TypeEnum tp = cast(TypeEnum)tparam;
+ if (t.sym == tp.sym)
+ visit(cast(Type)t);
+ else
+ result = MATCH.nomatch;
+ return;
+ }
+ Type tb = t.toBasetype();
+ if (tb.ty == tparam.ty || tb.ty == Tsarray && tparam.ty == Taarray)
+ {
+ result = deduceType(tb, sc, tparam, parameters, dedtypes, wm);
+ if (result == MATCH.exact)
+ result = MATCH.convert;
+ return;
+ }
+ visit(cast(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(ref BaseClass b, Scope* sc, Type tparam, TemplateParameters* parameters, Objects* dedtypes, Objects* best, ref 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
+ auto tmpdedtypes = new Objects(dedtypes.dim);
+ memcpy(tmpdedtypes.tdata(), dedtypes.tdata(), dedtypes.dim * (void*).sizeof);
+
+ auto t = new TypeInstance(Loc.initial, parti);
+ MATCH m = deduceType(t, sc, tparam, parameters, tmpdedtypes);
+ if (m > MATCH.nomatch)
+ {
+ // If this is the first ever match, it becomes our best estimate
+ if (numBaseClassMatches == 0)
+ memcpy(best.tdata(), tmpdedtypes.tdata(), tmpdedtypes.dim * (void*).sizeof);
+ else
+ for (size_t k = 0; k < tmpdedtypes.dim; ++k)
+ {
+ // If we've found more than one possible type for a parameter,
+ // mark it as unknown.
+ if ((*tmpdedtypes)[k] != (*best)[k])
+ (*best)[k] = (*dedtypes)[k];
+ }
+ ++numBaseClassMatches;
+ }
+ }
+
+ // Now recursively test the inherited interfaces
+ foreach (ref bi; b.baseInterfaces)
+ {
+ deduceBaseClassParameters(bi, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+ }
+ }
+
+ override 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)
+ {
+ auto tx = new TypeInstance(Loc.initial, 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 != MATCH.nomatch)
+ {
+ result = m;
+ return;
+ }
+ }
+
+ /* Match things like:
+ * S!(T).foo
+ */
+ TypeInstance tpi = cast(TypeInstance)tparam;
+ if (tpi.idents.dim)
+ {
+ RootObject id = tpi.idents[tpi.idents.dim - 1];
+ if (id.dyncast() == DYNCAST.identifier && t.sym.ident.equals(cast(Identifier)id))
+ {
+ Type tparent = t.sym.parent.getType();
+ if (tparent)
+ {
+ /* Slice off the .foo in S!(T).foo
+ */
+ tpi.idents.dim--;
+ result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm);
+ tpi.idents.dim++;
+ return;
+ }
+ }
+ }
+
+ // If it matches exactly or via implicit conversion, we're done
+ visit(cast(Type)t);
+ if (result != MATCH.nomatch)
+ 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
+ auto best = new Objects(dedtypes.dim);
+
+ ClassDeclaration s = t.sym;
+ while (s && s.baseclasses.dim > 0)
+ {
+ // Test the base class
+ deduceBaseClassParameters(*(*s.baseclasses)[0], sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+
+ // Test the interfaces inherited by the base class
+ foreach (b; s.interfaces)
+ {
+ deduceBaseClassParameters(*b, sc, tparam, parameters, dedtypes, best, numBaseClassMatches);
+ }
+ s = (*s.baseclasses)[0].sym;
+ }
+
+ if (numBaseClassMatches == 0)
+ {
+ result = MATCH.nomatch;
+ return;
+ }
+
+ // If we got at least one match, copy the known types into dedtypes
+ memcpy(dedtypes.tdata(), best.tdata(), best.dim * (void*).sizeof);
+ result = MATCH.convert;
+ return;
+ }
+
+ // Extra check
+ if (tparam && tparam.ty == Tclass)
+ {
+ TypeClass tp = cast(TypeClass)tparam;
+
+ //printf("\t%d\n", (MATCH) t.implicitConvTo(tp));
+ if (wm && t.deduceWild(tparam, false))
+ {
+ result = MATCH.constant;
+ return;
+ }
+ result = t.implicitConvTo(tp);
+ return;
+ }
+ visit(cast(Type)t);
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.deduceType(e = %s)\n", e.toChars());
+ size_t i = templateParameterLookup(tparam, parameters);
+ if (i == IDX_NOTFOUND || (cast(TypeIdentifier)tparam).idents.dim > 0)
+ {
+ if (e == emptyArrayElement && tparam.ty == Tarray)
+ {
+ Type tn = (cast(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 = MATCH.exact;
+ return;
+ }
+ if (tp.defaultType)
+ {
+ tp.defaultType.accept(this);
+ return;
+ }
+ }
+
+ /* Returns `true` if `t` is a reference type, or an array of reference types
+ */
+ bool isTopRef(Type t)
+ {
+ auto tb = t.baseElemOf();
+ return tb.ty == Tclass ||
+ tb.ty == Taarray ||
+ tb.ty == Tstruct && tb.hasPointers();
+ }
+
+ Type at = cast(Type)(*dedtypes)[i];
+ Type tt;
+ if (ubyte wx = deduceWildHelper(e.type, &tt, tparam))
+ {
+ *wm |= wx;
+ result = MATCH.constant;
+ }
+ else if (MATCH m = deduceTypeHelper(e.type, &tt, tparam))
+ {
+ result = m;
+ }
+ else if (!isTopRef(e.type))
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=15653
+ * In IFTI, recognize top-qualifier conversions
+ * through the value copy, e.g.
+ * int --> immutable(int)
+ * immutable(string[]) --> immutable(string)[]
+ */
+ tt = e.type.mutableOf();
+ result = MATCH.convert;
+ }
+ else
+ return; // nomatch
+
+ // expression vs (none)
+ if (!at)
+ {
+ (*dedtypes)[i] = new TypeDeduced(tt, e, tparam);
+ return;
+ }
+
+ TypeDeduced xt = null;
+ if (at.ty == Tnone)
+ {
+ xt = cast(TypeDeduced)at;
+ at = xt.tded;
+ }
+
+ // From previous matched expressions to current deduced type
+ MATCH match1 = xt ? xt.matchAll(tt) : MATCH.nomatch;
+
+ // From current expressions to previous deduced type
+ Type pt = at.addMod(tparam.mod);
+ if (*wm)
+ pt = pt.substWildTo(*wm);
+ MATCH match2 = e.implicitConvTo(pt);
+
+ if (match1 > MATCH.nomatch && match2 > MATCH.nomatch)
+ {
+ if (at.implicitConvTo(tt) == MATCH.nomatch)
+ match1 = MATCH.nomatch; // Prefer at
+ else if (tt.implicitConvTo(at) == MATCH.nomatch)
+ match2 = MATCH.nomatch; // 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 = MATCH.nomatch;
+ else
+ match2 = MATCH.nomatch;
+ }
+ else if (at.isMutable())
+ {
+ if (tt.mod == 0) // Prefer unshared
+ match2 = MATCH.nomatch;
+ else
+ match1 = MATCH.nomatch;
+ }
+ //printf("tt = %s, at = %s\n", tt.toChars(), at.toChars());
+ }
+ else
+ {
+ match1 = MATCH.nomatch;
+ match2 = MATCH.nomatch;
+ }
+ }
+ if (match1 > MATCH.nomatch)
+ {
+ // Prefer current match: tt
+ if (xt)
+ xt.update(tt, e, tparam);
+ else
+ (*dedtypes)[i] = tt;
+ result = match1;
+ return;
+ }
+ if (match2 > MATCH.nomatch)
+ {
+ // 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 = MATCH.nomatch;
+ }
+
+ MATCH deduceEmptyArrayElement()
+ {
+ if (!emptyArrayElement)
+ {
+ emptyArrayElement = new IdentifierExp(Loc.initial, Id.p); // dummy
+ emptyArrayElement.type = Type.tvoid;
+ }
+ assert(tparam.ty == Tarray);
+
+ Type tn = (cast(TypeNext)tparam).next;
+ return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm);
+ }
+
+ override void visit(NullExp e)
+ {
+ if (tparam.ty == Tarray && e.type.ty == Tnull)
+ {
+ // tparam:T[] <- e:null (void[])
+ result = deduceEmptyArrayElement();
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(StringExp e)
+ {
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ // Consider compile-time known boundaries
+ e.type.nextOf().sarrayOf(e.len).accept(this);
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=20092
+ if (e.elements && e.elements.dim && e.type.toBasetype().nextOf().ty == Tvoid)
+ {
+ result = deduceEmptyArrayElement();
+ return;
+ }
+ if ((!e.elements || !e.elements.dim) && e.type.toBasetype().nextOf().ty == Tvoid && tparam.ty == Tarray)
+ {
+ // tparam:T[] <- e:[] (void[])
+ result = deduceEmptyArrayElement();
+ return;
+ }
+
+ if (tparam.ty == Tarray && e.elements && e.elements.dim)
+ {
+ Type tn = (cast(TypeDArray)tparam).next;
+ result = MATCH.exact;
+ if (e.basis)
+ {
+ MATCH m = deduceType(e.basis, sc, tn, parameters, dedtypes, wm);
+ if (m < result)
+ result = m;
+ }
+ foreach (el; *e.elements)
+ {
+ if (result == MATCH.nomatch)
+ break;
+ 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 = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ // Consider compile-time known boundaries
+ e.type.nextOf().sarrayOf(e.elements.dim).accept(this);
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ if (tparam.ty == Taarray && e.keys && e.keys.dim)
+ {
+ TypeAArray taa = cast(TypeAArray)tparam;
+ result = MATCH.exact;
+ foreach (i, key; *e.keys)
+ {
+ MATCH m1 = deduceType(key, sc, taa.index, parameters, dedtypes, wm);
+ if (m1 < result)
+ result = m1;
+ if (result == MATCH.nomatch)
+ break;
+ MATCH m2 = deduceType((*e.values)[i], sc, taa.next, parameters, dedtypes, wm);
+ if (m2 < result)
+ result = m2;
+ if (result == MATCH.nomatch)
+ break;
+ }
+ return;
+ }
+ visit(cast(Expression)e);
+ }
+
+ override 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())
+ return;
+ auto tof = to.nextOf().isTypeFunction();
+ if (!tof)
+ return;
+
+ // Parameter types inference from 'tof'
+ assert(e.td._scope);
+ TypeFunction tf = cast(TypeFunction)e.fd.type;
+ //printf("\ttof = %s\n", tof.toChars());
+ //printf("\ttf = %s\n", tf.toChars());
+ const dim = tf.parameterList.length;
+
+ if (tof.parameterList.length != dim || tof.parameterList.varargs != tf.parameterList.varargs)
+ return;
+
+ auto tiargs = new Objects();
+ tiargs.reserve(e.td.parameters.dim);
+
+ foreach (tp; *e.td.parameters)
+ {
+ size_t u = 0;
+ foreach (i, p; tf.parameterList)
+ {
+ if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident)
+ break;
+ ++u;
+ }
+ assert(u < dim);
+ Parameter pto = tof.parameterList[u];
+ if (!pto)
+ break;
+ Type t = pto.type.syntaxCopy(); // https://issues.dlang.org/show_bug.cgi?id=11774
+ if (reliesOnTemplateParameters(t, (*parameters)[inferStart .. parameters.dim]))
+ return;
+ t = t.typeSemantic(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;
+
+ auto ti = new TemplateInstance(e.loc, e.td, tiargs);
+ Expression ex = (new ScopeExp(e.loc, ti)).expressionSemantic(e.td._scope);
+
+ // Reset inference target for the later re-semantic
+ e.fd.treq = null;
+
+ if (ex.op == TOK.error)
+ return;
+ if (ex.op != TOK.function_)
+ 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 == TOK.reserved && t.ty == Tpointer && tparam.ty == Tdelegate)
+ {
+ TypeFunction tf = cast(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);
+ }
+
+ override void visit(SliceExp e)
+ {
+ Type taai;
+ if (e.type.ty == Tarray && (tparam.ty == Tsarray || tparam.ty == Taarray && (taai = (cast(TypeAArray)tparam).index).ty == Tident && (cast(TypeIdentifier)taai).idents.dim == 0))
+ {
+ // Consider compile-time known boundaries
+ if (Type tsa = toStaticArrayType(e))
+ {
+ tsa.accept(this);
+ if (result > MATCH.convert)
+ result = MATCH.convert; // match with implicit conversion at most
+ return;
+ }
+ }
+ visit(cast(Expression)e);
+ }
+
+ override void visit(CommaExp e)
+ {
+ e.e2.accept(this);
+ }
+ }
+
+ scope DeduceType v = new DeduceType(sc, tparam, parameters, dedtypes, wm, inferStart, ignoreAliasThis);
+ if (Type t = isType(o))
+ t.accept(v);
+ else if (Expression e = isExpression(o))
+ {
+ assert(wm);
+ e.accept(v);
+ }
+ else
+ assert(0);
+ return v.result;
+}
+
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ * iStart = Start index of tparams to limit the tested parameters. If it's
+ * nonzero, tparams[0..iStart] will be excluded from the test target.
+ */
+bool reliesOnTident(Type t, TemplateParameters* tparams, size_t iStart = 0)
+{
+ return reliesOnTemplateParameters(t, (*tparams)[0 .. tparams.dim]);
+}
+
+/***********************************************************
+ * Check whether the type t representation relies on one or more the template parameters.
+ * Params:
+ * t = Tested type, if null, returns false.
+ * tparams = Template parameters.
+ */
+private bool reliesOnTemplateParameters(Type t, TemplateParameter[] tparams)
+{
+ bool visitVector(TypeVector t)
+ {
+ return t.basetype.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitAArray(TypeAArray t)
+ {
+ return t.next.reliesOnTemplateParameters(tparams) ||
+ t.index.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitFunction(TypeFunction t)
+ {
+ foreach (i, fparam; t.parameterList)
+ {
+ if (fparam.type.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+ return t.next.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitIdentifier(TypeIdentifier t)
+ {
+ foreach (tp; tparams)
+ {
+ if (tp.ident.equals(t.ident))
+ return true;
+ }
+ return false;
+ }
+
+ bool visitInstance(TypeInstance t)
+ {
+ foreach (tp; tparams)
+ {
+ if (t.tempinst.name == tp.ident)
+ return true;
+ }
+
+ if (t.tempinst.tiargs)
+ foreach (arg; *t.tempinst.tiargs)
+ {
+ if (Type ta = isType(arg))
+ {
+ if (ta.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool visitTypeof(TypeTypeof t)
+ {
+ //printf("TypeTypeof.reliesOnTemplateParameters('%s')\n", t.toChars());
+ return t.exp.reliesOnTemplateParameters(tparams);
+ }
+
+ bool visitTuple(TypeTuple t)
+ {
+ if (t.arguments)
+ foreach (arg; *t.arguments)
+ {
+ if (arg.type.reliesOnTemplateParameters(tparams))
+ return true;
+ }
+
+ return false;
+ }
+
+ if (!t)
+ return false;
+
+ Type tb = t.toBasetype();
+ switch (tb.ty)
+ {
+ case Tvector: return visitVector(tb.isTypeVector());
+ case Taarray: return visitAArray(tb.isTypeAArray());
+ case Tfunction: return visitFunction(tb.isTypeFunction());
+ case Tident: return visitIdentifier(tb.isTypeIdentifier());
+ case Tinstance: return visitInstance(tb.isTypeInstance());
+ case Ttypeof: return visitTypeof(tb.isTypeTypeof());
+ case Ttuple: return visitTuple(tb.isTypeTuple());
+ case Tenum: return false;
+ default: return tb.nextOf().reliesOnTemplateParameters(tparams);
+ }
+}
+
+/***********************************************************
+ * Check whether the expression representation relies on one or more the template parameters.
+ * Params:
+ * e = expression to test
+ * tparams = Template parameters.
+ * Returns:
+ * true if it does
+ */
+private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparams)
+{
+ extern (C++) final class ReliesOnTemplateParameters : Visitor
+ {
+ alias visit = Visitor.visit;
+ public:
+ TemplateParameter[] tparams;
+ bool result;
+
+ extern (D) this(TemplateParameter[] tparams)
+ {
+ this.tparams = tparams;
+ }
+
+ override void visit(Expression e)
+ {
+ //printf("Expression.reliesOnTemplateParameters('%s')\n", e.toChars());
+ }
+
+ override void visit(IdentifierExp e)
+ {
+ //printf("IdentifierExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (tp; tparams)
+ {
+ if (e.ident == tp.ident)
+ {
+ result = true;
+ return;
+ }
+ }
+ }
+
+ override void visit(TupleExp e)
+ {
+ //printf("TupleExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.exps)
+ {
+ foreach (ea; *e.exps)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(ArrayLiteralExp e)
+ {
+ //printf("ArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
+ {
+ foreach (el; *e.elements)
+ {
+ el.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(AssocArrayLiteralExp e)
+ {
+ //printf("AssocArrayLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ foreach (ek; *e.keys)
+ {
+ ek.accept(this);
+ if (result)
+ return;
+ }
+ foreach (ev; *e.values)
+ {
+ ev.accept(this);
+ if (result)
+ return;
+ }
+ }
+
+ override void visit(StructLiteralExp e)
+ {
+ //printf("StructLiteralExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.elements)
+ {
+ foreach (ea; *e.elements)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(TypeExp e)
+ {
+ //printf("TypeExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.type.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(NewExp e)
+ {
+ //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.thisexp)
+ e.thisexp.accept(this);
+ if (!result && e.newargs)
+ {
+ foreach (ea; *e.newargs)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ result = e.newtype.reliesOnTemplateParameters(tparams);
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(NewAnonClassExp e)
+ {
+ //printf("NewAnonClassExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
+
+ override void visit(FuncExp e)
+ {
+ //printf("FuncExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = true;
+ }
+
+ override void visit(TypeidExp e)
+ {
+ //printf("TypeidExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (auto ea = isExpression(e.obj))
+ ea.accept(this);
+ else if (auto ta = isType(e.obj))
+ result = ta.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(TraitsExp e)
+ {
+ //printf("TraitsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ if (e.args)
+ {
+ foreach (oa; *e.args)
+ {
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(IsExp e)
+ {
+ //printf("IsExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ result = e.targ.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(UnaExp e)
+ {
+ //printf("UnaExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ }
+
+ override void visit(DotTemplateInstanceExp e)
+ {
+ //printf("DotTemplateInstanceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.ti.tiargs)
+ {
+ foreach (oa; *e.ti.tiargs)
+ {
+ if (auto ea = isExpression(oa))
+ ea.accept(this);
+ else if (auto ta = isType(oa))
+ result = ta.reliesOnTemplateParameters(tparams);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(CallExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ {
+ ea.accept(this);
+ if (result)
+ return;
+ }
+ }
+ }
+
+ override void visit(CastExp e)
+ {
+ //printf("CallExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ // e.to can be null for cast() with no type
+ if (!result && e.to)
+ result = e.to.reliesOnTemplateParameters(tparams);
+ }
+
+ override void visit(SliceExp e)
+ {
+ //printf("SliceExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.lwr)
+ e.lwr.accept(this);
+ if (!result && e.upr)
+ e.upr.accept(this);
+ }
+
+ override void visit(IntervalExp e)
+ {
+ //printf("IntervalExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.lwr.accept(this);
+ if (!result)
+ e.upr.accept(this);
+ }
+
+ override void visit(ArrayExp e)
+ {
+ //printf("ArrayExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ visit(cast(UnaExp)e);
+ if (!result && e.arguments)
+ {
+ foreach (ea; *e.arguments)
+ ea.accept(this);
+ }
+ }
+
+ override void visit(BinExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.e1.accept(this);
+ if (!result)
+ e.e2.accept(this);
+ }
+
+ override void visit(CondExp e)
+ {
+ //printf("BinExp.reliesOnTemplateParameters('%s')\n", e.toChars());
+ e.econd.accept(this);
+ if (!result)
+ visit(cast(BinExp)e);
+ }
+ }
+
+ scope ReliesOnTemplateParameters v = new ReliesOnTemplateParameters(tparams);
+ e.accept(v);
+ return v.result;
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateParameter
+ */
+extern (C++) class TemplateParameter : ASTNode
+{
+ Loc loc;
+ Identifier ident;
+
+ /* True if this is a part of precedent parameter specialization pattern.
+ *
+ * template A(T : X!TL, alias X, TL...) {}
+ * // X and TL are dependent template parameter
+ *
+ * A dependent template parameter should return MATCH.exact in matchArg()
+ * to respect the match level of the corresponding precedent parameter.
+ */
+ bool dependent;
+
+ /* ======================== TemplateParameter =============================== */
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ this.loc = loc;
+ this.ident = ident;
+ }
+
+ TemplateTypeParameter isTemplateTypeParameter()
+ {
+ return null;
+ }
+
+ TemplateValueParameter isTemplateValueParameter()
+ {
+ return null;
+ }
+
+ TemplateAliasParameter isTemplateAliasParameter()
+ {
+ return null;
+ }
+
+ TemplateThisParameter isTemplateThisParameter()
+ {
+ return null;
+ }
+
+ TemplateTupleParameter isTemplateTupleParameter()
+ {
+ return null;
+ }
+
+ abstract TemplateParameter syntaxCopy();
+
+ abstract bool declareParameter(Scope* sc);
+
+ abstract void print(RootObject oarg, RootObject oded);
+
+ abstract RootObject specialization();
+
+ abstract RootObject defaultArg(Loc instLoc, Scope* sc);
+
+ abstract bool hasDefaultArg();
+
+ override const(char)* toChars() const
+ {
+ return this.ident.toChars();
+ }
+
+ override DYNCAST dyncast() const pure @nogc nothrow @safe
+ {
+ return DYNCAST.templateparameter;
+ }
+
+ /* Create dummy argument based on parameter.
+ */
+ abstract RootObject dummyArg();
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateTypeParameter
+ * Syntax:
+ * ident : specType = defaultType
+ */
+extern (C++) class TemplateTypeParameter : TemplateParameter
+{
+ Type specType; // if !=null, this is the type specialization
+ Type defaultType;
+
+ extern (D) __gshared Type tdummy = null;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType)
+ {
+ super(loc, ident);
+ this.specType = specType;
+ this.defaultType = defaultType;
+ }
+
+ override final TemplateTypeParameter isTemplateTypeParameter()
+ {
+ return this;
+ }
+
+ override TemplateTypeParameter syntaxCopy()
+ {
+ return new TemplateTypeParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+ }
+
+ override final bool declareParameter(Scope* sc)
+ {
+ //printf("TemplateTypeParameter.declareParameter('%s')\n", ident.toChars());
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ override final void 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());
+ }
+
+ override final RootObject specialization()
+ {
+ return specType;
+ }
+
+ override final RootObject defaultArg(Loc instLoc, Scope* sc)
+ {
+ Type t = defaultType;
+ if (t)
+ {
+ t = t.syntaxCopy();
+ t = t.typeSemantic(loc, sc); // use the parameter loc
+ }
+ return t;
+ }
+
+ override final bool hasDefaultArg()
+ {
+ return defaultType !is null;
+ }
+
+ override final RootObject dummyArg()
+ {
+ Type t = specType;
+ if (!t)
+ {
+ // Use this for alias-parameter's too (?)
+ if (!tdummy)
+ tdummy = new TypeIdentifier(loc, ident);
+ t = tdummy;
+ }
+ return t;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateThisParameter
+ * Syntax:
+ * this ident : specType = defaultType
+ */
+extern (C++) final class TemplateThisParameter : TemplateTypeParameter
+{
+ extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType)
+ {
+ super(loc, ident, specType, defaultType);
+ }
+
+ override TemplateThisParameter isTemplateThisParameter()
+ {
+ return this;
+ }
+
+ override TemplateThisParameter syntaxCopy()
+ {
+ return new TemplateThisParameter(loc, ident, specType ? specType.syntaxCopy() : null, defaultType ? defaultType.syntaxCopy() : null);
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateValueParameter
+ * Syntax:
+ * valType ident : specValue = defaultValue
+ */
+extern (C++) final class TemplateValueParameter : TemplateParameter
+{
+ Type valType;
+ Expression specValue;
+ Expression defaultValue;
+
+ extern (D) __gshared Expression[void*] edummies;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type valType,
+ Expression specValue, Expression defaultValue)
+ {
+ super(loc, ident);
+ this.valType = valType;
+ this.specValue = specValue;
+ this.defaultValue = defaultValue;
+ }
+
+ override TemplateValueParameter isTemplateValueParameter()
+ {
+ return this;
+ }
+
+ override TemplateValueParameter syntaxCopy()
+ {
+ return new TemplateValueParameter(loc, ident,
+ valType.syntaxCopy(),
+ specValue ? specValue.syntaxCopy() : null,
+ defaultValue ? defaultValue.syntaxCopy() : null);
+ }
+
+ override bool declareParameter(Scope* sc)
+ {
+ auto v = new VarDeclaration(loc, valType, ident, null);
+ v.storage_class = STC.templateparameter;
+ return sc.insert(v) !is null;
+ }
+
+ override void print(RootObject oarg, 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");
+ }
+
+ override RootObject specialization()
+ {
+ return specValue;
+ }
+
+ override RootObject defaultArg(Loc instLoc, Scope* sc)
+ {
+ Expression e = defaultValue;
+ if (e)
+ {
+ e = e.syntaxCopy();
+ uint olderrs = global.errors;
+ if ((e = e.expressionSemantic(sc)) is null)
+ return null;
+ if ((e = resolveProperties(sc, e)) is null)
+ return null;
+ e = e.resolveLoc(instLoc, sc); // use the instantiated loc
+ e = e.optimize(WANTvalue);
+ if (global.errors != olderrs)
+ e = ErrorExp.get();
+ }
+ return e;
+ }
+
+ override bool hasDefaultArg()
+ {
+ return defaultValue !is null;
+ }
+
+ override RootObject dummyArg()
+ {
+ Expression e = specValue;
+ if (!e)
+ {
+ // Create a dummy value
+ auto pe = cast(void*)valType in edummies;
+ if (!pe)
+ {
+ e = valType.defaultInit(Loc.initial);
+ edummies[cast(void*)valType] = e;
+ }
+ else
+ e = *pe;
+ }
+ return e;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateAliasParameter
+ * Syntax:
+ * specType ident : specAlias = defaultAlias
+ */
+extern (C++) final class TemplateAliasParameter : TemplateParameter
+{
+ Type specType;
+ RootObject specAlias;
+ RootObject defaultAlias;
+
+ extern (D) __gshared Dsymbol sdummy = null;
+
+ extern (D) this(const ref Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias)
+ {
+ super(loc, ident);
+ this.specType = specType;
+ this.specAlias = specAlias;
+ this.defaultAlias = defaultAlias;
+ }
+
+ override TemplateAliasParameter isTemplateAliasParameter()
+ {
+ return this;
+ }
+
+ override TemplateAliasParameter syntaxCopy()
+ {
+ return new TemplateAliasParameter(loc, ident, specType ? specType.syntaxCopy() : null, objectSyntaxCopy(specAlias), objectSyntaxCopy(defaultAlias));
+ }
+
+ override bool declareParameter(Scope* sc)
+ {
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s\n", ident.toChars());
+ Dsymbol sa = isDsymbol(oded);
+ assert(sa);
+ printf("\tParameter alias: %s\n", sa.toChars());
+ }
+
+ override RootObject specialization()
+ {
+ return specAlias;
+ }
+
+ override RootObject defaultArg(Loc instLoc, 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;
+ }
+
+ override bool hasDefaultArg()
+ {
+ return defaultAlias !is null;
+ }
+
+ override RootObject dummyArg()
+ {
+ RootObject s = specAlias;
+ if (!s)
+ {
+ if (!sdummy)
+ sdummy = new Dsymbol();
+ s = sdummy;
+ }
+ return s;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#TemplateSequenceParameter
+ * Syntax:
+ * ident ...
+ */
+extern (C++) final class TemplateTupleParameter : TemplateParameter
+{
+ extern (D) this(const ref Loc loc, Identifier ident)
+ {
+ super(loc, ident);
+ }
+
+ override TemplateTupleParameter isTemplateTupleParameter()
+ {
+ return this;
+ }
+
+ override TemplateTupleParameter syntaxCopy()
+ {
+ return new TemplateTupleParameter(loc, ident);
+ }
+
+ override bool declareParameter(Scope* sc)
+ {
+ auto ti = new TypeIdentifier(loc, ident);
+ Declaration ad = new AliasDeclaration(loc, ident, ti);
+ return sc.insert(ad) !is null;
+ }
+
+ override void print(RootObject oarg, RootObject oded)
+ {
+ printf(" %s... [", ident.toChars());
+ Tuple v = isTuple(oded);
+ assert(v);
+
+ //printf("|%d| ", v.objects.dim);
+ foreach (i, o; v.objects)
+ {
+ if (i)
+ printf(", ");
+
+ 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");
+ }
+
+ override RootObject specialization()
+ {
+ return null;
+ }
+
+ override RootObject defaultArg(Loc instLoc, Scope* sc)
+ {
+ return null;
+ }
+
+ override bool hasDefaultArg()
+ {
+ return false;
+ }
+
+ override RootObject dummyArg()
+ {
+ return null;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template.html#explicit_tmp_instantiation
+ * Given:
+ * foo!(args) =>
+ * name = foo
+ * tiargs = args
+ */
+extern (C++) class TemplateInstance : ScopeDsymbol
+{
+ Identifier name;
+
+ // Array of Types/Expressions of template
+ // instance arguments [int*, char, 10*10]
+ Objects* tiargs;
+
+ // Array of Types/Expressions corresponding
+ // to TemplateDeclaration.parameters
+ // [int, char, 100]
+ Objects tdtypes;
+
+ // Modules imported by this template instance
+ Modules importedModules;
+
+ Dsymbol tempdecl; // referenced by foo.bar.abc
+ Dsymbol enclosing; // if referencing local symbols, this is the context
+ Dsymbol aliasdecl; // !=null if instance is an alias for its sole member
+ TemplateInstance inst; // refer to existing instance
+ ScopeDsymbol argsym; // argument symbol table
+ size_t hash; // cached result of toHash()
+ Expressions* fargs; // for function template, these are the function arguments
+
+ TemplateInstances* deferred;
+
+ Module memberOf; // if !null, then this TemplateInstance appears in memberOf.members[]
+
+ // Used to determine the instance needs code generation.
+ // Note that these are inaccurate until semantic analysis phase completed.
+ TemplateInstance tinst; // enclosing template instance
+ TemplateInstance tnext; // non-first instantiated instances
+ Module minst; // the top module that instantiated this instance
+
+ private ushort _nest; // for recursive pretty printing detection, 3 MSBs reserved for flags (below)
+ ubyte inuse; // for recursive expansion detection
+
+ private enum Flag : uint
+ {
+ semantictiargsdone = 1u << (_nest.sizeof * 8 - 1), // MSB of _nest
+ havetempdecl = semantictiargsdone >> 1,
+ gagged = semantictiargsdone >> 2,
+ available = gagged - 1 // always last flag minus one, 1s for all available bits
+ }
+
+ extern(D) final @safe @property pure nothrow @nogc
+ {
+ ushort nest() const { return _nest & Flag.available; }
+ void nestUp() { assert(nest() < Flag.available); ++_nest; }
+ void nestDown() { assert(nest() > 0); --_nest; }
+ /// has semanticTiargs() been done?
+ bool semantictiargsdone() const { return (_nest & Flag.semantictiargsdone) != 0; }
+ void semantictiargsdone(bool x)
+ {
+ if (x) _nest |= Flag.semantictiargsdone;
+ else _nest &= ~Flag.semantictiargsdone;
+ }
+ /// if used second constructor
+ bool havetempdecl() const { return (_nest & Flag.havetempdecl) != 0; }
+ void havetempdecl(bool x)
+ {
+ if (x) _nest |= Flag.havetempdecl;
+ else _nest &= ~Flag.havetempdecl;
+ }
+ /// if the instantiation is done with error gagging
+ bool gagged() const { return (_nest & Flag.gagged) != 0; }
+ void gagged(bool x)
+ {
+ if (x) _nest |= Flag.gagged;
+ else _nest &= ~Flag.gagged;
+ }
+ }
+
+ extern (D) this(const ref Loc loc, Identifier ident, Objects* tiargs)
+ {
+ super(loc, null);
+ static if (LOG)
+ {
+ printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null");
+ }
+ this.name = ident;
+ this.tiargs = tiargs;
+ }
+
+ /*****************
+ * This constructor is only called when we figured out which function
+ * template to instantiate.
+ */
+ extern (D) this(const ref Loc loc, TemplateDeclaration td, Objects* tiargs)
+ {
+ super(loc, null);
+ static if (LOG)
+ {
+ printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars());
+ }
+ this.name = td.ident;
+ this.tiargs = tiargs;
+ this.tempdecl = td;
+ this.semantictiargsdone = true;
+ this.havetempdecl = true;
+ assert(tempdecl._scope);
+ }
+
+ extern (D) static Objects* arraySyntaxCopy(Objects* objs)
+ {
+ Objects* a = null;
+ if (objs)
+ {
+ a = new Objects(objs.dim);
+ foreach (i, o; *objs)
+ (*a)[i] = objectSyntaxCopy(o);
+ }
+ return a;
+ }
+
+ override TemplateInstance syntaxCopy(Dsymbol s)
+ {
+ TemplateInstance ti = s ? cast(TemplateInstance)s : new TemplateInstance(loc, name, null);
+ ti.tiargs = arraySyntaxCopy(tiargs);
+ TemplateDeclaration td;
+ if (inst && tempdecl && (td = tempdecl.isTemplateDeclaration()) !is null)
+ td.ScopeDsymbol.syntaxCopy(ti);
+ else
+ ScopeDsymbol.syntaxCopy(ti);
+ return ti;
+ }
+
+ // resolve real symbol
+ override final Dsymbol toAlias()
+ {
+ static if (LOG)
+ {
+ printf("TemplateInstance.toAlias()\n");
+ }
+ 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;
+ }
+
+ override const(char)* kind() const
+ {
+ return "template instance";
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ *ps = null;
+ return true;
+ }
+
+ override const(char)* toChars() const
+ {
+ OutBuffer buf;
+ toCBufferInstance(this, &buf);
+ return buf.extractChars();
+ }
+
+ override final const(char)* toPrettyCharsHelper()
+ {
+ OutBuffer buf;
+ toCBufferInstance(this, &buf, true);
+ return buf.extractChars();
+ }
+
+ /**************************************
+ * Given an error instantiating the TemplateInstance,
+ * give the nested TemplateInstance instantiations that got
+ * us here. Those are a list threaded into the nested scopes.
+ */
+ extern(D) final void printInstantiationTrace(Classification cl = Classification.error)
+ {
+ if (global.gag)
+ return;
+
+ // Print full trace for verbose mode, otherwise only short traces
+ const(uint) max_shown = !global.params.verbose ? 6 : uint.max;
+ const(char)* format = "instantiated from here: `%s`";
+
+ // This returns a function pointer
+ scope printFn = () {
+ final switch (cl)
+ {
+ case Classification.error:
+ return &errorSupplemental;
+ case Classification.warning:
+ return &warningSupplemental;
+ case Classification.deprecation:
+ return &deprecationSupplemental;
+ case Classification.gagged, Classification.tip:
+ assert(0);
+ }
+ }();
+
+ // determine instantiation depth and number of recursive instantiations
+ int n_instantiations = 1;
+ int n_totalrecursions = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ {
+ ++n_instantiations;
+ // Set error here as we don't want it to depend on the number of
+ // entries that are being printed.
+ if (cl == Classification.error ||
+ (cl == Classification.warning && global.params.warnings == DiagnosticReporting.error) ||
+ (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error))
+ cur.errors = true;
+
+ // 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;
+ }
+
+ if (n_instantiations <= max_shown)
+ {
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ printFn(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)
+ {
+ if (cur.tinst && cur.tempdecl && cur.tinst.tempdecl && cur.tempdecl.loc.equals(cur.tinst.tempdecl.loc))
+ {
+ ++recursionDepth;
+ }
+ else
+ {
+ if (recursionDepth)
+ printFn(cur.loc, "%d recursive instantiations from here: `%s`", recursionDepth + 2, cur.toChars());
+ else
+ printFn(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.
+ uint i = 0;
+ for (TemplateInstance cur = this; cur; cur = cur.tinst)
+ {
+ if (i == max_shown / 2)
+ printFn(cur.loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown);
+
+ if (i < max_shown / 2 || i >= n_instantiations - max_shown + max_shown / 2)
+ printFn(cur.loc, format, cur.toChars());
+ ++i;
+ }
+ }
+ }
+
+ /*************************************
+ * Lazily generate identifier for template instance.
+ * This is because 75% of the ident's are never needed.
+ */
+ override final Identifier getIdent()
+ {
+ if (!ident && inst && !errors)
+ ident = genIdent(tiargs); // need an identifier for name mangling purposes.
+ return ident;
+ }
+
+ /*************************************
+ * Compare proposed template instantiation with existing template instantiation.
+ * Note that this is not commutative because of the auto ref check.
+ * Params:
+ * ti = existing template instantiation
+ * Returns:
+ * true for match
+ */
+ final bool equalsx(TemplateInstance ti)
+ {
+ //printf("this = %p, ti = %p\n", this, ti);
+ assert(tdtypes.dim == ti.tdtypes.dim);
+
+ // Nesting must match
+ if (enclosing != ti.enclosing)
+ {
+ //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : "");
+ goto Lnotequals;
+ }
+ //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars());
+
+ if (!arrayObjectMatch(&tdtypes, &ti.tdtypes))
+ goto Lnotequals;
+
+ /* Template functions may have different instantiations based on
+ * "auto ref" parameters.
+ */
+ if (auto fd = ti.toAlias().isFuncDeclaration())
+ {
+ if (!fd.errors)
+ {
+ auto 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 & STC.autoref) // if "auto ref"
+ {
+ Expression farg = fargs && j < fargs.dim ? (*fargs)[j] : fparam.defaultArg;
+ if (!farg)
+ goto Lnotequals;
+ if (farg.isLvalue())
+ {
+ if (!(fparam.storageClass & STC.ref_))
+ goto Lnotequals; // auto ref's don't match
+ }
+ else
+ {
+ if (fparam.storageClass & STC.ref_)
+ goto Lnotequals; // auto ref's don't match
+ }
+ }
+ }
+ }
+ }
+ return true;
+
+ Lnotequals:
+ return false;
+ }
+
+ final size_t toHash()
+ {
+ if (!hash)
+ {
+ hash = cast(size_t)cast(void*)enclosing;
+ hash += arrayObjectHash(&tdtypes);
+ hash += hash == 0;
+ }
+ return hash;
+ }
+
+ /**
+ 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.
+ */
+ final bool isDiscardable()
+ {
+ if (aliasdecl is null)
+ return false;
+
+ auto v = aliasdecl.isVarDeclaration();
+ if (v is null)
+ return false;
+
+ if (!(v.storage_class & STC.manifest))
+ 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() !is 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
+ foreach(member; *members)
+ {
+ 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.
+ */
+ final bool 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())
+ {
+ 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)
+ {
+ auto r = tinst.needsCodegen();
+ minst = tinst.minst; // cache result
+ return r;
+ }
+ if (tnext)
+ {
+ auto 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;
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=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 https://issues.dlang.org/show_bug.cgi?id=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;
+ }
+ }
+
+ /**********************************************
+ * 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.
+ */
+ extern (D) final bool 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;
+ }
+ static if (LOG)
+ {
+ printf("It's an instance of '%s' kind '%s'\n", s.toChars(), s.kind());
+ if (s.parent)
+ printf("s.parent = '%s'\n", s.parent.toChars());
+ }
+ 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()) !is 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;
+ }
+ }
+
+ // The template might originate from a selective import which implies that
+ // s is a lowered AliasDeclaration of the actual TemplateDeclaration.
+ // This is the last place where we see the deprecated alias because it is
+ // stripped below, so check if the selective import was deprecated.
+ // See https://issues.dlang.org/show_bug.cgi?id=20840.
+ if (s.isAliasDeclaration())
+ s.checkDeprecated(this.loc, sc);
+
+ if (!updateTempDecl(sc, s))
+ {
+ return false;
+ }
+ }
+ assert(tempdecl);
+
+ // Look for forward references
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ if (td.semanticRun == PASS.init)
+ {
+ if (td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ Ungag ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.init)
+ {
+ error("`%s` forward references template declaration `%s`",
+ toChars(), td.toChars());
+ return 1;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ 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.
+ */
+ extern (D) final bool updateTempDecl(Scope* sc, Dsymbol s)
+ {
+ if (!s)
+ return tempdecl !is null;
+
+ Identifier id = name;
+ s = s.toAlias();
+
+ /* If an OverloadSet, look for a unique member that is a template declaration
+ */
+ if (OverloadSet os = s.isOverloadSet())
+ {
+ s = null;
+ foreach (s2; os.a)
+ {
+ 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;
+ }
+ }
+
+ if (OverDeclaration od = s.isOverDeclaration())
+ {
+ 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();
+
+ // We're done
+ if (tempdecl)
+ return true;
+
+ // Error already issued, just return `false`
+ if (!s.parent && global.errors)
+ return false;
+
+ if (!s.parent && s.getType())
+ {
+ Dsymbol s2 = s.getType().toDsymbol(sc);
+ if (!s2)
+ {
+ .error(loc, "`%s` is not a valid template instance, because `%s` is not a template declaration but a type (`%s == %s`)", toChars(), id.toChars(), id.toChars(), s.getType.kind());
+ return false;
+ }
+ // because s can be the alias created for a TemplateParameter
+ const AliasDeclaration ad = s.isAliasDeclaration();
+ version (none)
+ {
+ if (ad && ad.isAliasedTemplateParameter())
+ printf("`%s` is an alias created from a template parameter\n", s.toChars());
+ }
+ if (!ad || !ad.isAliasedTemplateParameter())
+ s = s2;
+ }
+
+ 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;
+ return true;
+ }
+ else
+ {
+ error("`%s` is not a template declaration, it is a %s", id.toChars(), s.kind());
+ 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.
+ */
+ extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags)
+ {
+ // Run semantic on each argument, place results in tiargs[]
+ //printf("+TemplateInstance.semanticTiargs()\n");
+ if (!tiargs)
+ return true;
+ bool err = false;
+ for (size_t j = 0; j < tiargs.dim; j++)
+ {
+ RootObject o = (*tiargs)[j];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+
+ //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta);
+ if (ta)
+ {
+ //printf("type %s\n", ta.toChars());
+
+ // It might really be an Expression or an Alias
+ ta.resolve(loc, sc, ea, ta, sa, (flags & 1) != 0);
+ if (ea)
+ goto Lexpr;
+ if (sa)
+ goto Ldsym;
+ if (ta is null)
+ {
+ assert(global.errors);
+ ta = Type.terror;
+ }
+
+ Ltype:
+ if (ta.ty == Ttuple)
+ {
+ // Expand tuple
+ TypeTuple tt = cast(TypeTuple)ta;
+ size_t dim = tt.arguments.dim;
+ tiargs.remove(j);
+ if (dim)
+ {
+ tiargs.reserve(dim);
+ foreach (i, arg; *tt.arguments)
+ {
+ if (flags & 2 && (arg.storageClass & STC.parameter))
+ 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 = ea.expressionSemantic(sc);
+
+ // must not interpret the args, excepting template parameters
+ if (ea.op != TOK.variable || ((cast(VarExp)ea).var.storage_class & STC.templateparameter))
+ {
+ ea = ea.optimize(WANTvalue);
+ }
+ }
+ else
+ {
+ sc = sc.startCTFE();
+ ea = ea.expressionSemantic(sc);
+ sc = sc.endCTFE();
+
+ if (ea.op == TOK.variable)
+ {
+ /* If the parameter is a function that is not called
+ * explicitly, i.e. `foo!func` as opposed to `foo!func()`,
+ * then it is a dsymbol, not the return value of `func()`
+ */
+ Declaration vd = (cast(VarExp)ea).var;
+ if (auto fd = vd.isFuncDeclaration())
+ {
+ sa = fd;
+ goto Ldsym;
+ }
+ /* Otherwise 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 = ErrorExp.get();
+ uint olderrs = global.errors;
+ ea = ea.ctfeInterpret();
+ if (global.errors != olderrs)
+ ea = ErrorExp.get();
+ }
+ }
+ //printf("-[%d] ea = %s %s\n", j, Token.toChars(ea.op), ea.toChars());
+ if (ea.op == TOK.tuple)
+ {
+ // Expand tuple
+ TupleExp te = cast(TupleExp)ea;
+ size_t dim = te.exps.dim;
+ tiargs.remove(j);
+ if (dim)
+ {
+ tiargs.reserve(dim);
+ foreach (i, exp; *te.exps)
+ tiargs.insert(j + i, exp);
+ }
+ j--;
+ continue;
+ }
+ if (ea.op == TOK.error)
+ {
+ err = true;
+ continue;
+ }
+ (*tiargs)[j] = ea;
+
+ if (ea.op == TOK.type)
+ {
+ ta = ea.type;
+ goto Ltype;
+ }
+ if (ea.op == TOK.scope_)
+ {
+ sa = (cast(ScopeExp)ea).sds;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.function_)
+ {
+ FuncExp fe = cast(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 == TOK.reserved && fe.type.ty == Tpointer)
+ {
+ // change to non-nested
+ fe.fd.tok = TOK.function_;
+ 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 == TOK.dotVariable && !(flags & 1))
+ {
+ // translate expression to dsymbol.
+ sa = (cast(DotVarExp)ea).var;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.template_)
+ {
+ sa = (cast(TemplateExp)ea).td;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.dotTemplateDeclaration && !(flags & 1))
+ {
+ // translate expression to dsymbol.
+ sa = (cast(DotTemplateExp)ea).td;
+ goto Ldsym;
+ }
+ if (ea.op == TOK.dot)
+ {
+ if (auto se = (cast(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 == PASS.init && td.literal)
+ {
+ td.dsymbolSemantic(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]);
+ }
+ version (none)
+ {
+ printf("-TemplateInstance.semanticTiargs()\n");
+ for (size_t j = 0; j < tiargs.dim; j++)
+ {
+ RootObject o = (*tiargs)[j];
+ Type ta = isType(o);
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ Tuple va = isTuple(o);
+ printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va);
+ }
+ }
+ return !err;
+ }
+
+ /**********************************
+ * 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.
+ */
+ extern (D) final bool 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;
+ }
+
+ /**********************************
+ * Find the TemplateDeclaration that matches this TemplateInstance best.
+ *
+ * Params:
+ * sc = the scope this TemplateInstance resides in
+ * fargs = function arguments in case of a template function, null otherwise
+ *
+ * Returns:
+ * `true` if a match was found, `false` otherwise
+ */
+ extern (D) final bool findBestMatch(Scope* sc, Expressions* fargs)
+ {
+ if (havetempdecl)
+ {
+ TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+ assert(tempdecl._scope);
+ // Deduce tdtypes
+ tdtypes.setDim(tempdecl.parameters.dim);
+ if (!tempdecl.matchWithInstance(sc, this, &tdtypes, fargs, 2))
+ {
+ error("incompatible arguments for template instantiation");
+ return false;
+ }
+ // TODO: Normalizing tiargs for https://issues.dlang.org/show_bug.cgi?id=7469 is necessary?
+ return true;
+ }
+
+ static if (LOG)
+ {
+ printf("TemplateInstance.findBestMatch()\n");
+ }
+
+ uint errs = global.errors;
+ TemplateDeclaration td_last = null;
+ Objects dedtypes;
+
+ /* Since there can be multiple TemplateDeclaration's with the same
+ * name, look for the best match.
+ */
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ TemplateDeclaration td_best;
+ TemplateDeclaration td_ambig;
+ MATCH m_best = MATCH.nomatch;
+
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+ if (td.inuse)
+ {
+ td.error(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.dim < tiargs.dim)
+ {
+ if (!td.isVariadic())
+ return 0;
+ }
+
+ dedtypes.setDim(td.parameters.dim);
+ dedtypes.zero();
+ assert(td.semanticRun != PASS.init);
+
+ MATCH m = td.matchWithInstance(sc, this, &dedtypes, fargs, 0);
+ //printf("matchWithInstance = %d\n", m);
+ if (m == MATCH.nomatch) // 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, fargs);
+ MATCH c2 = td_best.leastAsSpecialized(sc, td, 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;
+ tdtypes.setDim(dedtypes.dim);
+ memcpy(tdtypes.tdata(), dedtypes.tdata(), tdtypes.dim * (void*).sizeof);
+ return 0;
+ });
+
+ if (td_ambig)
+ {
+ .error(loc, "%s `%s.%s` matches more than one template declaration:\n%s: `%s`\nand\n%s: `%s`",
+ td_best.kind(), td_best.parent.toPrettyChars(), td_best.ident.toChars(),
+ td_best.loc.toChars(), td_best.toChars(),
+ td_ambig.loc.toChars(), td_ambig.toChars());
+ return false;
+ }
+ if (td_best)
+ {
+ if (!td_last)
+ td_last = td_best;
+ else if (td_last != td_best)
+ {
+ ScopeDsymbol.multiplyDefined(loc, td_last, td_best);
+ return false;
+ }
+ }
+ }
+
+ if (td_last)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=7469
+ * Normalize tiargs by using corresponding deduced
+ * template value parameters and tuples for the correct mangling.
+ *
+ * By doing this before hasNestedArgs, CTFEable local variable will be
+ * accepted as a value parameter. For example:
+ *
+ * void foo() {
+ * struct S(int n) {} // non-global template
+ * const int num = 1; // CTFEable local variable
+ * S!num s; // S!1 is instantiated, not S!num
+ * }
+ */
+ size_t dim = td_last.parameters.dim - (td_last.isVariadic() ? 1 : 0);
+ for (size_t i = 0; i < dim; i++)
+ {
+ if (tiargs.dim <= i)
+ tiargs.push(tdtypes[i]);
+ assert(i < tiargs.dim);
+
+ auto tvp = (*td_last.parameters)[i].isTemplateValueParameter();
+ if (!tvp)
+ continue;
+ assert(tdtypes[i]);
+ // tdtypes[i] is already normalized to the required type in matchArg
+
+ (*tiargs)[i] = tdtypes[i];
+ }
+ if (td_last.isVariadic() && tiargs.dim == dim && tdtypes[dim])
+ {
+ Tuple va = isTuple(tdtypes[dim]);
+ assert(va);
+ tiargs.pushSlice(va.objects[]);
+ }
+ }
+ else if (errors && inst)
+ {
+ // instantiation was failed with error reporting
+ assert(global.errors);
+ return false;
+ }
+ else
+ {
+ auto 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
+ const(char)* msg = "does not match template declaration";
+ const(char)* tip;
+ const tmsg = tdecl.toCharsNoConstraints();
+ const cmsg = tdecl.getConstraintEvalError(tip);
+ if (cmsg)
+ {
+ error("%s `%s`\n%s", msg, tmsg, cmsg);
+ if (tip)
+ .tip(tip);
+ }
+ else
+ {
+ error("%s `%s`", msg, tmsg);
+
+ if (tdecl.parameters.dim == tiargs.dim)
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=7352
+ // print additional information, e.g. `foo` is not a type
+ foreach (i, param; *tdecl.parameters)
+ {
+ MATCH match = param.matchArg(loc, sc, tiargs, i, tdecl.parameters, &dedtypes, null);
+ auto arg = (*tiargs)[i];
+ auto sym = arg.isDsymbol;
+ auto exp = arg.isExpression;
+
+ if (exp)
+ exp = exp.optimize(WANTvalue);
+
+ if (match == MATCH.nomatch &&
+ ((sym && sym.isFuncDeclaration) ||
+ (exp && exp.isVarExp)))
+ {
+ if (param.isTemplateTypeParameter)
+ errorSupplemental(loc, "`%s` is not a type", arg.toChars);
+ else if (auto tvp = param.isTemplateValueParameter)
+ errorSupplemental(loc, "`%s` is not of a value of type `%s`",
+ arg.toChars, tvp.valType.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;
+
+ static if (LOG)
+ {
+ printf("\tIt's a match with template declaration '%s'\n", tempdecl.toChars());
+ }
+ 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.
+ */
+ extern (D) final bool needsTypeInference(Scope* sc, int flag = 0)
+ {
+ //printf("TemplateInstance.needsTypeInference() %s\n", toChars());
+ if (semanticRun != PASS.init)
+ return false;
+
+ uint olderrs = global.errors;
+ Objects dedtypes;
+ size_t count = 0;
+
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+ if (td.inuse)
+ {
+ td.error(loc, "recursive template expansion");
+ return 1;
+ }
+
+ /* If any of the overloaded template declarations need inference,
+ * then return true
+ */
+ if (!td.onemember)
+ return 0;
+ if (auto td2 = td.onemember.isTemplateDeclaration())
+ {
+ if (!td2.onemember || !td2.onemember.isFuncDeclaration())
+ return 0;
+ if (tiargs.dim >= td.parameters.dim - (td.isVariadic() ? 1 : 0))
+ return 0;
+ return 1;
+ }
+ auto fd = td.onemember.isFuncDeclaration();
+ if (!fd || fd.type.ty != Tfunction)
+ return 0;
+
+ foreach (tp; *td.parameters)
+ {
+ if (tp.isTemplateThisParameter())
+ return 1;
+ }
+
+ /* Determine if the instance arguments, tiargs, are all that is necessary
+ * to instantiate the template.
+ */
+ //printf("tp = %p, td.parameters.dim = %d, tiargs.dim = %d\n", tp, td.parameters.dim, tiargs.dim);
+ auto tf = cast(TypeFunction)fd.type;
+ if (tf.parameterList.length)
+ {
+ auto tp = td.isVariadic();
+ if (tp && td.parameters.dim > 1)
+ return 1;
+
+ if (!tp && tiargs.dim < td.parameters.dim)
+ {
+ // Can remain tiargs be filled by default arguments?
+ foreach (size_t i; tiargs.dim .. td.parameters.dim)
+ {
+ if (!(*td.parameters)[i].hasDefaultArg())
+ return 1;
+ }
+ }
+
+ foreach (i, fparam; tf.parameterList)
+ {
+ // 'auto ref' needs inference.
+ if (fparam.storageClass & STC.auto_)
+ return 1;
+ }
+ }
+
+ if (!flag)
+ {
+ /* Calculate the need for overload resolution.
+ * When only one template can match with tiargs, inference is not necessary.
+ */
+ dedtypes.setDim(td.parameters.dim);
+ dedtypes.zero();
+ if (td.semanticRun == PASS.init)
+ {
+ if (td._scope)
+ {
+ // Try to fix forward reference. Ungag errors while doing so.
+ Ungag ungag = td.ungagSpeculative();
+ td.dsymbolSemantic(td._scope);
+ }
+ if (td.semanticRun == PASS.init)
+ {
+ error("`%s` forward references template declaration `%s`", toChars(), td.toChars());
+ return 1;
+ }
+ }
+ MATCH m = td.matchWithInstance(sc, this, &dedtypes, null, 0);
+ if (m == MATCH.nomatch)
+ return 0;
+ }
+
+ /* If there is more than one function template which matches, we may
+ * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430)
+ */
+ return ++count > 1 ? 1 : 0;
+ });
+ if (r)
+ return true;
+ }
+
+ if (olderrs != global.errors)
+ {
+ if (!global.gag)
+ {
+ errorSupplemental(loc, "while looking for match for `%s`", toChars());
+ semanticRun = PASS.semanticdone;
+ 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;
+ */
+ extern (D) final bool hasNestedArgs(Objects* args, bool isstatic)
+ {
+ int nested = 0;
+ //printf("TemplateInstance.hasNestedArgs('%s')\n", tempdecl.ident.toChars());
+
+ // arguments from parent instances are also accessible
+ if (!enclosing)
+ {
+ if (TemplateInstance ti = tempdecl.toParent().isTemplateInstance())
+ enclosing = ti.enclosing;
+ }
+
+ /* A nested instance happens when an argument references a local
+ * symbol that is on the stack.
+ */
+ foreach (o; *args)
+ {
+ Expression ea = isExpression(o);
+ Dsymbol sa = isDsymbol(o);
+ Tuple va = isTuple(o);
+ if (ea)
+ {
+ if (ea.op == TOK.variable)
+ {
+ sa = (cast(VarExp)ea).var;
+ goto Lsa;
+ }
+ if (ea.op == TOK.this_)
+ {
+ sa = (cast(ThisExp)ea).var;
+ goto Lsa;
+ }
+ if (ea.op == TOK.function_)
+ {
+ if ((cast(FuncExp)ea).td)
+ sa = (cast(FuncExp)ea).td;
+ else
+ sa = (cast(FuncExp)ea).fd;
+ goto Lsa;
+ }
+ // Emulate Expression.toMangleBuffer call that had exist in TemplateInstance.genIdent.
+ if (ea.op != TOK.int64 && ea.op != TOK.float64 && ea.op != TOK.complex80 && ea.op != TOK.null_ && ea.op != TOK.string_ && ea.op != TOK.arrayLiteral && ea.op != TOK.assocArrayLiteral && ea.op != TOK.structLiteral)
+ {
+ 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 & STC.manifest) && (!d.isFuncDeclaration() || d.isFuncDeclaration().isNested()) && !isTemplateMixin()))
+ {
+ Dsymbol dparent = sa.toParent2();
+ if (!dparent)
+ goto L1;
+ else 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 if (va)
+ {
+ nested |= cast(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[]
+ */
+ extern (D) final Dsymbols* 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.
+ */
+ static Dsymbol getStrictEnclosing(TemplateInstance ti)
+ {
+ do
+ {
+ if (ti.enclosing)
+ return ti.enclosing;
+ ti = ti.tempdecl.isInstantiated();
+ } while (ti);
+ return null;
+ }
+
+ Dsymbol enc = 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 is mi) // already a member
+ {
+ debug // make sure it really is a member
+ {
+ auto a = mi.members;
+ for (size_t i = 0; 1; ++i)
+ {
+ assert(i != a.dim);
+ if (this == (*a)[i])
+ break;
+ }
+ }
+ return null;
+ }
+
+ Dsymbols* a = mi.members;
+ a.push(this);
+ memberOf = mi;
+ if (mi.semanticRun >= PASS.semantic2done && mi.isRoot())
+ Module.addDeferredSemantic2(this);
+ if (mi.semanticRun >= PASS.semantic3done && mi.isRoot())
+ Module.addDeferredSemantic3(this);
+ return a;
+ }
+
+ /****************************************************
+ * Declare parameters of template instance, initialize them with the
+ * template instance arguments.
+ */
+ extern (D) final void declareParameters(Scope* sc)
+ {
+ TemplateDeclaration tempdecl = this.tempdecl.isTemplateDeclaration();
+ assert(tempdecl);
+
+ //printf("TemplateInstance.declareParameters()\n");
+ foreach (i, o; tdtypes) // initializer for tp
+ {
+ TemplateParameter tp = (*tempdecl.parameters)[i];
+ //printf("\ttdtypes[%d] = %p\n", i, o);
+ tempdecl.declareParameter(sc, tp, o);
+ }
+ }
+
+ /****************************************
+ * This instance needs an identifier for name mangling purposes.
+ * Create one by taking the template declaration name and adding
+ * the type signature for it.
+ */
+ extern (D) final Identifier genIdent(Objects* args)
+ {
+ //printf("TemplateInstance.genIdent('%s')\n", tempdecl.ident.toChars());
+ assert(args is tiargs);
+ OutBuffer buf;
+ mangleToBuffer(this, &buf);
+ //printf("\tgenIdent = %s\n", buf.peekChars());
+ return Identifier.idPool(buf[]);
+ }
+
+ extern (D) final void expandMembers(Scope* sc2)
+ {
+ members.foreachDsymbol( (s) { s.setScope (sc2); } );
+
+ members.foreachDsymbol( (s) { s.importAll(sc2); } );
+
+ void symbolDg(Dsymbol s)
+ {
+ //printf("\t semantic on '%s' %p kind %s in '%s'\n", s.toChars(), s, s.kind(), this.toChars());
+ //printf("test: enclosing = %d, sc2.parent = %s\n", enclosing, sc2.parent.toChars());
+ //if (enclosing)
+ // s.parent = sc.parent;
+ //printf("test3: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ s.dsymbolSemantic(sc2);
+ //printf("test4: enclosing = %d, s.parent = %s\n", enclosing, s.parent.toChars());
+ Module.runDeferredSemantic();
+ }
+
+ members.foreachDsymbol(&symbolDg);
+ }
+
+ extern (D) final void tryExpandMembers(Scope* sc2)
+ {
+ __gshared 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--;
+ }
+
+ extern (D) final void trySemantic3(Scope* sc2)
+ {
+ // extracted to a function to allow windows SEH to work without destructors in the same function
+ __gshared 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;
+ }
+
+ override final inout(TemplateInstance) isTemplateInstance() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/**************************************
+ * 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))
+ {
+ foreach (obj; tup.objects)
+ {
+ unSpeculative(sc, obj);
+ }
+ 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 !is null || sc.minst is 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);
+}
+
+/**********************************
+ * 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 == TOK.tuple || e.op == TOK.scope_ ||
+ e.op == TOK.type || e.op == TOK.dotType ||
+ e.op == TOK.template_ || e.op == TOK.dotTemplateDeclaration ||
+ e.op == TOK.function_ || e.op == TOK.error ||
+ e.op == TOK.this_ || e.op == TOK.super_ ||
+ e.op == TOK.dot)
+ return false;
+
+ if (e.op != TOK.dotVariable)
+ 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 = (cast(DotVarExp)e).var.isFuncDeclaration();
+ if (f)
+ return false;
+
+ while (e.op == TOK.dotVariable)
+ {
+ e = (cast(DotVarExp)e).e1;
+ }
+ // this.x.y and super.x.y couldn't possibly be valid values.
+ if (e.op == TOK.this_ || e.op == TOK.super_)
+ return false;
+
+ // e.type.x could be an alias
+ if (e.op == TOK.dotType)
+ return false;
+
+ // var.x.y is the only other possible form of alias
+ if (e.op != TOK.variable)
+ return true;
+
+ VarDeclaration v = (cast(VarExp)e).var.isVarDeclaration();
+ // func.x.y is not an alias
+ if (!v)
+ return true;
+
+ // https://issues.dlang.org/show_bug.cgi?id=16685
+ // var.x.y where var is a constant available at compile time
+ if (v.storage_class & STC.manifest)
+ return true;
+
+ // TODO: Should we force CTFE if it is a global constant?
+ return false;
+}
+
+/***********************************************************
+ * https://dlang.org/spec/template-mixin.html
+ * Syntax:
+ * mixin MixinTemplateName [TemplateArguments] [Identifier];
+ */
+extern (C++) final class TemplateMixin : TemplateInstance
+{
+ TypeQualified tqual;
+
+ extern (D) this(const ref Loc loc, Identifier ident, TypeQualified tqual, Objects* tiargs)
+ {
+ super(loc,
+ tqual.idents.dim ? cast(Identifier)tqual.idents[tqual.idents.dim - 1] : (cast(TypeIdentifier)tqual).ident,
+ tiargs ? tiargs : new Objects());
+ //printf("TemplateMixin(ident = '%s')\n", ident ? ident.toChars() : "");
+ this.ident = ident;
+ this.tqual = tqual;
+ }
+
+ override TemplateInstance syntaxCopy(Dsymbol s)
+ {
+ auto tm = new TemplateMixin(loc, ident, tqual.syntaxCopy(), tiargs);
+ return TemplateInstance.syntaxCopy(tm);
+ }
+
+ override const(char)* kind() const
+ {
+ return "mixin";
+ }
+
+ override bool oneMember(Dsymbol* ps, Identifier ident)
+ {
+ return Dsymbol.oneMember(ps, ident);
+ }
+
+ override bool hasPointers()
+ {
+ //printf("TemplateMixin.hasPointers() %s\n", toChars());
+ return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0;
+ }
+
+ override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
+ {
+ //printf("TemplateMixin.setFieldOffset() %s\n", toChars());
+ if (_scope) // if fwd reference
+ dsymbolSemantic(this, null); // try to resolve it
+
+ members.foreachDsymbol( (s) { s.setFieldOffset(ad, fieldState, isunion); } );
+ }
+
+ override const(char)* toChars() const
+ {
+ OutBuffer buf;
+ toCBufferInstance(this, &buf);
+ return buf.extractChars();
+ }
+
+ extern (D) bool 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;
+ foreach (i, sym; os.a)
+ {
+ Dsymbol s2 = sym.isTemplateDeclaration();
+ if (s2)
+ {
+ if (ds)
+ {
+ tempdecl = os;
+ break;
+ }
+ ds = s2;
+ }
+ }
+ }
+ if (!tempdecl)
+ {
+ error("`%s` isn't a template", s.toChars());
+ return false;
+ }
+ }
+ assert(tempdecl);
+
+ // Look for forward references
+ auto tovers = tempdecl.isOverloadSet();
+ foreach (size_t oi; 0 .. tovers ? tovers.a.dim : 1)
+ {
+ Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl;
+ int r = overloadApply(dstart, (Dsymbol s)
+ {
+ auto td = s.isTemplateDeclaration();
+ if (!td)
+ return 0;
+
+ if (td.semanticRun == PASS.init)
+ {
+ if (td._scope)
+ td.dsymbolSemantic(td._scope);
+ else
+ {
+ semanticRun = PASS.init;
+ return 1;
+ }
+ }
+ return 0;
+ });
+ if (r)
+ return false;
+ }
+ return true;
+ }
+
+ override inout(TemplateMixin) isTemplateMixin() inout
+ {
+ return this;
+ }
+
+ override void accept(Visitor v)
+ {
+ v.visit(this);
+ }
+}
+
+/************************************
+ * This struct is needed for TemplateInstance to be the key in an associative array.
+ * Fixing https://issues.dlang.org/show_bug.cgi?id=15812 and
+ * https://issues.dlang.org/show_bug.cgi?id=15813 would make it unnecessary.
+ */
+struct TemplateInstanceBox
+{
+ TemplateInstance ti;
+
+ this(TemplateInstance ti)
+ {
+ this.ti = ti;
+ this.ti.toHash();
+ assert(this.ti.hash);
+ }
+
+ size_t toHash() const @trusted pure nothrow
+ {
+ assert(ti.hash);
+ return ti.hash;
+ }
+
+ bool opEquals(ref const TemplateInstanceBox s) @trusted const
+ {
+ bool res = void;
+ if (ti.inst && s.ti.inst)
+ /* This clause is only used when an instance with errors
+ * is replaced with a correct instance.
+ */
+ res = ti is s.ti;
+ else
+ /* Used when a proposed instance is used to see if there's
+ * an existing instance.
+ */
+ res = (cast()s.ti).equalsx(cast()ti);
+
+ debug (FindExistingInstance) ++(res ? nHits : nCollisions);
+ return res;
+ }
+
+ debug (FindExistingInstance)
+ {
+ __gshared uint nHits, nCollisions;
+
+ shared static ~this()
+ {
+ printf("debug (FindExistingInstance) TemplateInstanceBox.equals hits: %u collisions: %u\n",
+ nHits, nCollisions);
+ }
+ }
+}
+
+/*******************************************
+ * 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 matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, size_t i, TemplateParameters* parameters, Objects* dedtypes, Declaration* psparam)
+{
+ MATCH matchArgNoMatch()
+ {
+ if (psparam)
+ *psparam = null;
+ return MATCH.nomatch;
+ }
+
+ MATCH matchArgParameter()
+ {
+ RootObject oarg;
+
+ if (i < tiargs.dim)
+ oarg = (*tiargs)[i];
+ else
+ {
+ // Get default argument instead
+ oarg = tp.defaultArg(instLoc, sc);
+ if (!oarg)
+ {
+ assert(i < dedtypes.dim);
+ // It might have already been deduced
+ oarg = (*dedtypes)[i];
+ if (!oarg)
+ return matchArgNoMatch();
+ }
+ }
+ return tp.matchArg(sc, oarg, i, parameters, dedtypes, psparam);
+ }
+
+ MATCH matchArgTuple(TemplateTupleParameter ttp)
+ {
+ /* The rest of the actual arguments (tiargs[]) form the match
+ * for the variadic parameter.
+ */
+ assert(i + 1 == dedtypes.dim); // must be the last one
+ Tuple ovar;
+
+ if (Tuple u = isTuple((*dedtypes)[i]))
+ {
+ // It has already been deduced
+ ovar = u;
+ }
+ else if (i + 1 == tiargs.dim && isTuple((*tiargs)[i]))
+ ovar = isTuple((*tiargs)[i]);
+ else
+ {
+ ovar = new Tuple();
+ //printf("ovar = %p\n", ovar);
+ if (i < tiargs.dim)
+ {
+ //printf("i = %d, tiargs.dim = %d\n", i, tiargs.dim);
+ ovar.objects.setDim(tiargs.dim - i);
+ foreach (j, ref obj; ovar.objects)
+ obj = (*tiargs)[i + j];
+ }
+ }
+ return ttp.matchArg(sc, ovar, i, parameters, dedtypes, psparam);
+ }
+
+ if (auto ttp = tp.isTemplateTupleParameter())
+ return matchArgTuple(ttp);
+ else
+ return matchArgParameter();
+}
+
+MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, Objects* dedtypes, Declaration* psparam)
+{
+ MATCH matchArgNoMatch()
+ {
+ //printf("\tm = %d\n", MATCH.nomatch);
+ if (psparam)
+ *psparam = null;
+ return MATCH.nomatch;
+ }
+
+ MATCH matchArgType(TemplateTypeParameter ttp)
+ {
+ //printf("TemplateTypeParameter.matchArg('%s')\n", ttp.ident.toChars());
+ MATCH m = MATCH.exact;
+ Type ta = isType(oarg);
+ if (!ta)
+ {
+ //printf("%s %p %p %p\n", oarg.toChars(), isExpression(oarg), isDsymbol(oarg), isTuple(oarg));
+ return matchArgNoMatch();
+ }
+ //printf("ta is %s\n", ta.toChars());
+
+ if (ttp.specType)
+ {
+ if (!ta || ta == TemplateTypeParameter.tdummy)
+ return matchArgNoMatch();
+
+ //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta.toChars(), ttp.specType.toChars());
+ MATCH m2 = deduceType(ta, sc, ttp.specType, parameters, dedtypes);
+ if (m2 == MATCH.nomatch)
+ {
+ //printf("\tfailed deduceType\n");
+ return matchArgNoMatch();
+ }
+
+ if (m2 < m)
+ m = m2;
+ if ((*dedtypes)[i])
+ {
+ Type t = cast(Type)(*dedtypes)[i];
+
+ if (ttp.dependent && !t.equals(ta)) // https://issues.dlang.org/show_bug.cgi?id=14357
+ return matchArgNoMatch();
+
+ /* 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 = cast(Type)(*dedtypes)[i];
+
+ if (!t.equals(ta))
+ {
+ //printf("t = %s ta = %s\n", t.toChars(), ta.toChars());
+ return matchArgNoMatch();
+ }
+ }
+ else
+ {
+ // So that matches with specializations are better
+ m = MATCH.convert;
+ }
+ }
+ (*dedtypes)[i] = ta;
+
+ if (psparam)
+ *psparam = new AliasDeclaration(ttp.loc, ttp.ident, ta);
+ //printf("\tm = %d\n", m);
+ return ttp.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgValue(TemplateValueParameter tvp)
+ {
+ //printf("TemplateValueParameter.matchArg('%s')\n", tvp.ident.toChars());
+ MATCH m = MATCH.exact;
+
+ 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())
+ return matchArgNoMatch();
+
+ ei = new VarExp(tvp.loc, f);
+ ei = ei.expressionSemantic(sc);
+
+ /* If a function is really property-like, and then
+ * it's CTFEable, ei will be a literal expression.
+ */
+ uint olderrors = global.startGagging();
+ ei = resolveProperties(sc, ei);
+ ei = ei.ctfeInterpret();
+ if (global.endGagging(olderrors) || ei.op == TOK.error)
+ return matchArgNoMatch();
+
+ /* https://issues.dlang.org/show_bug.cgi?id=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 = MATCH.convert;
+ }
+
+ if (ei && ei.op == TOK.variable)
+ {
+ // Resolve const variables that we had skipped earlier
+ ei = ei.ctfeInterpret();
+ }
+
+ //printf("\tvalType: %s, ty = %d\n", tvp.valType.toChars(), tvp.valType.ty);
+ vt = tvp.valType.typeSemantic(tvp.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 == MATCH.nomatch)
+ return matchArgNoMatch();
+ ei = ei.implicitCastTo(sc, vt);
+ ei = ei.ctfeInterpret();
+ }
+
+ if (tvp.specValue)
+ {
+ if (ei is null || (cast(void*)ei.type in TemplateValueParameter.edummies &&
+ TemplateValueParameter.edummies[cast(void*)ei.type] == ei))
+ return matchArgNoMatch();
+
+ Expression e = tvp.specValue;
+
+ sc = sc.startCTFE();
+ e = e.expressionSemantic(sc);
+ e = resolveProperties(sc, e);
+ sc = sc.endCTFE();
+ e = e.implicitCastTo(sc, vt);
+ e = e.ctfeInterpret();
+
+ ei = ei.syntaxCopy();
+ sc = sc.startCTFE();
+ ei = ei.expressionSemantic(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))
+ return matchArgNoMatch();
+ }
+ else
+ {
+ if ((*dedtypes)[i])
+ {
+ // Must match already deduced value
+ Expression e = cast(Expression)(*dedtypes)[i];
+ if (!ei || !ei.equals(e))
+ return matchArgNoMatch();
+ }
+ }
+ (*dedtypes)[i] = ei;
+
+ if (psparam)
+ {
+ Initializer _init = new ExpInitializer(tvp.loc, ei);
+ Declaration sparam = new VarDeclaration(tvp.loc, vt, tvp.ident, _init);
+ sparam.storage_class = STC.manifest;
+ *psparam = sparam;
+ }
+ return tvp.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgAlias(TemplateAliasParameter tap)
+ {
+ //printf("TemplateAliasParameter.matchArg('%s')\n", tap.ident.toChars());
+ MATCH m = MATCH.exact;
+ Type ta = isType(oarg);
+ RootObject sa = ta && !ta.deco ? null : getDsymbol(oarg);
+ Expression ea = isExpression(oarg);
+ if (ea && (ea.op == TOK.this_ || ea.op == TOK.super_))
+ sa = (cast(ThisExp)ea).var;
+ else if (ea && ea.op == TOK.scope_)
+ sa = (cast(ScopeExp)ea).sds;
+ if (sa)
+ {
+ if ((cast(Dsymbol)sa).isAggregateDeclaration())
+ m = MATCH.convert;
+
+ /* specType means the alias must be a declaration with a type
+ * that matches specType.
+ */
+ if (tap.specType)
+ {
+ Declaration d = (cast(Dsymbol)sa).isDeclaration();
+ if (!d)
+ return matchArgNoMatch();
+ if (!d.type.equals(tap.specType))
+ return matchArgNoMatch();
+ }
+ }
+ else
+ {
+ sa = oarg;
+ if (ea)
+ {
+ if (tap.specType)
+ {
+ if (!ea.type.equals(tap.specType))
+ return matchArgNoMatch();
+ }
+ }
+ else if (ta && ta.ty == Tinstance && !tap.specAlias)
+ {
+ /* Specialized parameter should be preferred
+ * 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)
+ {
+ /* https://issues.dlang.org/show_bug.cgi?id=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 = MATCH.convert;
+ }
+ else
+ return matchArgNoMatch();
+ }
+
+ if (tap.specAlias)
+ {
+ if (sa == TemplateAliasParameter.sdummy)
+ return matchArgNoMatch();
+ // check specialization if template arg is a symbol
+ Dsymbol sx = isDsymbol(sa);
+ if (sa != tap.specAlias && sx)
+ {
+ Type talias = isType(tap.specAlias);
+ if (!talias)
+ return matchArgNoMatch();
+
+ TemplateInstance ti = sx.isTemplateInstance();
+ if (!ti && sx.parent)
+ {
+ ti = sx.parent.isTemplateInstance();
+ if (ti && ti.name != sx.ident)
+ return matchArgNoMatch();
+ }
+ if (!ti)
+ return matchArgNoMatch();
+
+ Type t = new TypeInstance(Loc.initial, ti);
+ MATCH m2 = deduceType(t, sc, talias, parameters, dedtypes);
+ if (m2 == MATCH.nomatch)
+ return matchArgNoMatch();
+ }
+ // check specialization if template arg is a type
+ else if (ta)
+ {
+ if (Type tspec = isType(tap.specAlias))
+ {
+ MATCH m2 = ta.implicitConvTo(tspec);
+ if (m2 == MATCH.nomatch)
+ return matchArgNoMatch();
+ }
+ else
+ {
+ error(tap.loc, "template parameter specialization for a type must be a type and not `%s`",
+ tap.specAlias.toChars());
+ return matchArgNoMatch();
+ }
+ }
+ }
+ else if ((*dedtypes)[i])
+ {
+ // Must match already deduced symbol
+ RootObject si = (*dedtypes)[i];
+ if (!sa || si != sa)
+ return matchArgNoMatch();
+ }
+ (*dedtypes)[i] = sa;
+
+ if (psparam)
+ {
+ if (Dsymbol s = isDsymbol(sa))
+ {
+ *psparam = new AliasDeclaration(tap.loc, tap.ident, s);
+ }
+ else if (Type t = isType(sa))
+ {
+ *psparam = new AliasDeclaration(tap.loc, tap.ident, t);
+ }
+ else
+ {
+ assert(ea);
+
+ // Declare manifest constant
+ Initializer _init = new ExpInitializer(tap.loc, ea);
+ auto v = new VarDeclaration(tap.loc, null, tap.ident, _init);
+ v.storage_class = STC.manifest;
+ v.dsymbolSemantic(sc);
+ *psparam = v;
+ }
+ }
+ return tap.dependent ? MATCH.exact : m;
+ }
+
+ MATCH matchArgTuple(TemplateTupleParameter ttp)
+ {
+ //printf("TemplateTupleParameter.matchArg('%s')\n", ttp.ident.toChars());
+ Tuple ovar = isTuple(oarg);
+ if (!ovar)
+ return MATCH.nomatch;
+ if ((*dedtypes)[i])
+ {
+ Tuple tup = isTuple((*dedtypes)[i]);
+ if (!tup)
+ return MATCH.nomatch;
+ if (!match(tup, ovar))
+ return MATCH.nomatch;
+ }
+ (*dedtypes)[i] = ovar;
+
+ if (psparam)
+ *psparam = new TupleDeclaration(ttp.loc, ttp.ident, &ovar.objects);
+ return ttp.dependent ? MATCH.exact : MATCH.convert;
+ }
+
+ if (auto ttp = tp.isTemplateTypeParameter())
+ return matchArgType(ttp);
+ else if (auto tvp = tp.isTemplateValueParameter())
+ return matchArgValue(tvp);
+ else if (auto tap = tp.isTemplateAliasParameter())
+ return matchArgAlias(tap);
+ else if (auto ttp = tp.isTemplateTupleParameter())
+ return matchArgTuple(ttp);
+ else
+ assert(0);
+}
+
+
+/***********************************************
+ * Collect and print statistics on template instantiations.
+ */
+struct TemplateStats
+{
+ __gshared TemplateStats[const void*] stats;
+
+ uint numInstantiations; // number of instantiations of the template
+ uint uniqueInstantiations; // number of unique instantiations of the template
+
+ TemplateInstances* allInstances;
+
+ /*******************************
+ * Add this instance
+ */
+ static void incInstance(const TemplateDeclaration td,
+ const TemplateInstance ti)
+ {
+ void log(ref TemplateStats ts)
+ {
+ if (ts.allInstances is null)
+ ts.allInstances = new TemplateInstances();
+ if (global.params.vtemplatesListInstances)
+ ts.allInstances.push(cast() ti);
+ }
+
+ // message(ti.loc, "incInstance %p %p", td, ti);
+ if (!global.params.vtemplates)
+ return;
+ if (!td)
+ return;
+ assert(ti);
+ if (auto ts = cast(const void*) td in stats)
+ {
+ log(*ts);
+ ++ts.numInstantiations;
+ }
+ else
+ {
+ stats[cast(const void*) td] = TemplateStats(1, 0);
+ log(stats[cast(const void*) td]);
+ }
+ }
+
+ /*******************************
+ * Add this unique instance
+ */
+ static void incUnique(const TemplateDeclaration td,
+ const TemplateInstance ti)
+ {
+ // message(ti.loc, "incUnique %p %p", td, ti);
+ if (!global.params.vtemplates)
+ return;
+ if (!td)
+ return;
+ assert(ti);
+ if (auto ts = cast(const void*) td in stats)
+ ++ts.uniqueInstantiations;
+ else
+ stats[cast(const void*) td] = TemplateStats(0, 1);
+ }
+}
+
+void printTemplateStats()
+{
+ static struct TemplateDeclarationStats
+ {
+ TemplateDeclaration td;
+ TemplateStats ts;
+ static int compare(scope const TemplateDeclarationStats* a,
+ scope const TemplateDeclarationStats* b) @safe nothrow @nogc pure
+ {
+ auto diff = b.ts.uniqueInstantiations - a.ts.uniqueInstantiations;
+ if (diff)
+ return diff;
+ else
+ return b.ts.numInstantiations - a.ts.numInstantiations;
+ }
+ }
+
+ if (!global.params.vtemplates)
+ return;
+
+ Array!(TemplateDeclarationStats) sortedStats;
+ sortedStats.reserve(TemplateStats.stats.length);
+ foreach (td_, ref ts; TemplateStats.stats)
+ {
+ sortedStats.push(TemplateDeclarationStats(cast(TemplateDeclaration) td_, ts));
+ }
+
+ sortedStats.sort!(TemplateDeclarationStats.compare);
+
+ foreach (const ref ss; sortedStats[])
+ {
+ if (global.params.vtemplatesListInstances &&
+ ss.ts.allInstances)
+ {
+ message(ss.td.loc,
+ "vtemplate: %u (%u distinct) instantiation(s) of template `%s` found, they are:",
+ ss.ts.numInstantiations,
+ ss.ts.uniqueInstantiations,
+ ss.td.toCharsNoConstraints());
+ foreach (const ti; (*ss.ts.allInstances)[])
+ {
+ if (ti.tinst) // if has enclosing instance
+ message(ti.loc, "vtemplate: implicit instance `%s`", ti.toChars());
+ else
+ message(ti.loc, "vtemplate: explicit instance `%s`", ti.toChars());
+ }
+ }
+ else
+ {
+ message(ss.td.loc,
+ "vtemplate: %u (%u distinct) instantiation(s) of template `%s` found",
+ ss.ts.numInstantiations,
+ ss.ts.uniqueInstantiations,
+ ss.td.toCharsNoConstraints());
+ }
+ }
+}
+
+/// Pair of MATCHes
+private struct MATCHpair
+{
+ MATCH mta; /// match template parameters by initial template arguments
+ MATCH mfa; /// match template parameters by inferred template arguments
+
+ debug this(MATCH mta, MATCH mfa)
+ {
+ assert(MATCH.min <= mta && mta <= MATCH.max);
+ assert(MATCH.min <= mfa && mfa <= MATCH.max);
+ this.mta = mta;
+ this.mfa = mfa;
+ }
+}