blob: 1f99c58decd7e3bdcb4cece102bf7bcfa03b7d35 [file] [log] [blame]
/**
* 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-2022 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
* License: $(LINK2 https://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.common.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 == EXP.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", EXPtoString(e1.op).ptr, e1.toChars());
printf("\te2 = %s '%s' %s\n", e2.type ? e2.type.toChars() : "null", EXPtoString(e2.op).ptr, 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 EXP.int64:
return cast(size_t) e.isIntegerExp().getInteger();
case EXP.float64:
return CTFloat.hash(e.isRealExp().value);
case EXP.complex80:
auto ce = e.isComplexExp();
return mixHash(CTFloat.hash(ce.toReal), CTFloat.hash(ce.toImaginary));
case EXP.identifier:
return cast(size_t)cast(void*) e.isIdentifierExp().ident;
case EXP.null_:
return cast(size_t)cast(void*) e.isNullExp().type;
case EXP.string_:
return calcHash(e.isStringExp.peekData());
case EXP.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 EXP.arrayLiteral:
{
auto ae = e.isArrayLiteralExp();
size_t hash;
foreach (i; 0 .. ae.elements.dim)
hash = mixHash(hash, expressionHash(ae[i]));
return hash;
}
case EXP.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 EXP.structLiteral:
{
auto se = e.isStructLiteralExp();
size_t hash;
foreach (elem; *se.elements)
hash = mixHash(hash, elem ? expressionHash(elem) : 0);
return hash;
}
case EXP.variable:
return cast(size_t)cast(void*) e.isVarExp().var;
case EXP.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(" ");
}
write(buf, (*parameters)[i]);
buf.writestring(" = ");
write(buf, tiargs[i]);
}
// write remaining variadic arguments on the last line
if (variadic)
{
if (i > 0)
{
buf.writeByte(',');
buf.writenl();
buf.writestring(" ");
}
write(buf, (*parameters)[end]);
buf.writestring(" = ");
buf.writeByte('(');
if (cast(int)tiargs.length - end > 0)
{
write(buf, tiargs[end]);
foreach (j; parameters.length .. tiargs.length)
{
buf.writestring(", ");
write(buf, 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(parameters.dim);
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.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())
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() && !(_scope.stc & STC.static_))
{
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 == EXP.error || farg.type.ty == Terror)
return nomatch();
if (!fparam.isLazy() && 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 == EXP.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.isLazy() && argtype.ty == Tvoid && farg.op != EXP.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 == EXP.string_)
{
StringExp se = cast(StringExp)farg;
argtype = se.type.nextOf().sarrayOf(se.len);
}
else if (farg.op == EXP.arrayLiteral)
{
ArrayLiteralExp ae = cast(ArrayLiteralExp)farg;
argtype = ae.type.nextOf().sarrayOf(ae.elements.dim);
}
else if (farg.op == EXP.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.
*
* We also save/restore sc.func.flags to avoid messing up
* attribute inference in the evaluation.
*/
const oldflags = sc.func ? sc.func.flags : 0;
auto e = resolveAliasThis(sc, farg, true);
if (sc.func)
sc.func.flags = oldflags;
if (e)
{
farg = e;
goto Lretry;
}
}
}
if (m > MATCH.nomatch && (fparam.storageClass & (STC.ref_ | STC.auto_)) == STC.ref_)
{
if (!farg.isLvalue())
{
if ((farg.op == EXP.string_ || farg.op == EXP.slice) && (prmtype.ty == Tsarray || prmtype.ty == Taarray))
{
// Allow conversion from T[lwr .. upr] to ref T[upr-lwr]
}
else if (global.params.rvalueRefParam == FeatureState.enabled)
{
// 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.isLazy() && 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 == EXP.type)
ta = ea.type;
else if (ea && ea.op == EXP.scope_)
sa = (cast(ScopeExp)ea).sds;
else if (ea && (ea.op == EXP.this_ || ea.op == EXP.super_))
sa = (cast(ThisExp)ea).var;
else if (ea && ea.op == EXP.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;
int firstIsBetter()
{
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;
}
if (mfa > m.last) return firstIsBetter();
if (mfa < m.last) return 0;
/* See if one of the matches overrides the other.
*/
assert(m.lastf);
if (m.lastf.overrides(fd)) return 0;
if (fd.overrides(m.lastf)) return firstIsBetter();
/* 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) return firstIsBetter();
if (c1 < c2) return 0;
}
/* 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)
{
return 0;
}
}
else if (firstCovariant == Covariant.yes || firstCovariant == Covariant.no)
{
return firstIsBetter();
}
}
/* 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)
return firstIsBetter();
if (!fd.fbody)
return 0;
}
// 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) return firstIsBetter();
if (tthis.mod == m.lastf.type.mod) return 0;
}
m.nextf = fd;
m.count++;
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.initial && td._scope)
{
// Try to fix forward reference. Ungag errors while doing so.
Ungag ungag = td.ungagSpeculative();
td.dsymbolSemantic(td._scope);
}
if (td.semanticRun == PASS.initial)
{
.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.initial);
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)