| /** | |
| * 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 = ≺ // 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.flags |= FUNCFLAG.inferRetType; | |
| // 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 == EXP.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 == 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.storageClass & STC.lazy_) && 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.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 == 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; | |
| 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.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 = ≺ // 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 == EXP.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 == EXP.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); | |
| auto m = deduceType(tx, sc, tparam, parameters, dedtypes, wm); | |
| // if we have a no match we still need to check alias this | |
| 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; | |
| } | |
| } | |
| } | |
| } | |
| // Extra check | |
| if (tparam && tparam.ty == Tstruct) | |
| { | |
| TypeStruct tp = cast(TypeStruct)tparam; | |
| //printf("\t%d\n", cast(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", cast(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 == EXP.error) | |
| return; | |
| if (ex.op != EXP.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); | |
| 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(const ref 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(const ref 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) | |
| { | |
| /* | |
| Do type semantic earlier. | |
| This means for certain erroneous value parameters | |
| their "type" can be known earlier and thus a better | |
| error message given. | |
| For example: | |
| `template test(x* x) {}` | |
| now yields "undefined identifier" rather than the opaque | |
| "variable `x` is used as a type". | |
| */ | |
| if (valType) | |
| valType = valType.typeSemantic(loc, 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(const ref 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(const ref 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(const ref 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() | |
| { | |
| // minst is finalized after the 1st invocation. | |
| // tnext and tinst are only needed for the 1st invocation and | |
| // cleared for further invocations. | |
| TemplateInstance tnext = this.tnext; | |
| TemplateInstance tinst = this.tinst; | |
| this.tnext = null; | |
| this.tinst = null; | |
| if (errors || (inst && inst.isDiscardable())) | |
| { | |
| minst = null; // mark as speculative | |
| return false; | |
| } | |
| if (global.params.allInst) | |
| { | |
| // Do codegen if there is an instantiation from a root module, to maximize link-ability. | |
| // Do codegen if `this` is instantiated from a root module. | |
| if (minst && minst.isRoot()) | |
| return true; | |
| // Do codegen if the ancestor needs it. | |
| if (tinst && tinst.needsCodegen()) | |
| { | |
| minst = tinst.minst; // cache result | |
| assert(minst); | |
| assert(minst.isRoot()); | |
| return true; | |
| } | |
| // Do codegen if a sibling needs it. | |
| if (tnext) | |
| { | |
| if (tnext.needsCodegen()) | |
| { | |
| minst = tnext.minst; // cache result | |
| assert(minst); | |
| assert(minst.isRoot()); | |
| return true; | |
| } | |
| else if (!minst && tnext.minst) | |
| { | |
| minst = tnext.minst; // cache result from non-speculative sibling | |
| return false; | |
| } | |
| } | |
| // Elide codegen because there's no instantiation from any root modules. | |
| return false; | |
| } | |
| else | |
| { | |
| // Prefer instantiations from non-root modules, to minimize object code size. | |
| /* If a TemplateInstance is ever instantiated from a non-root module, | |
| * 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. | |
| * | |
| * => Elide codegen if there is at least one instantiation from a non-root module | |
| * which doesn't import any root modules. | |
| */ | |
| // If the ancestor isn't speculative, | |
| // 1. do codegen if the ancestor needs it | |
| // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree) | |
| if (tinst) | |
| { | |
| const needsCodegen = tinst.needsCodegen(); // sets tinst.minst | |
| if (tinst.minst) // not speculative | |
| { | |
| minst = tinst.minst; // cache result | |
| return needsCodegen; | |
| } | |
| } | |
| // Elide codegen if `this` doesn't need it. | |
| if (minst && !minst.isRoot() && !minst.rootImports()) | |
| return false; | |
| // Elide codegen if a (non-speculative) sibling doesn't need it. | |
| if (tnext) | |
| { | |
| const needsCodegen = tnext.needsCodegen(); // sets tnext.minst | |
| if (tnext.minst) // not speculative | |
| { | |
| if (!needsCodegen) | |
| { | |
| minst = tnext.minst; // cache result | |
| assert(!minst.isRoot() && !minst.rootImports()); | |
| return false; | |
| } | |
| else if (!minst) | |
| { | |
| minst = tnext.minst; // cache result from non-speculative sibling | |
| return true; | |
| } | |
| } | |
| } | |
| // Unless `this` is still speculative (=> all further siblings speculative too), | |
| // do codegen because we found no guaranteed-codegen'd non-root instantiation. | |
| return minst !is null; | |
| } | |
| } | |
| /********************************************** | |
| * 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.initial) | |
| { | |
| 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.initial) | |
| { | |
| 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, EXPtoString(ea.op).ptr, ea.toChars()); | |
| if (flags & 1) // only used by __traits | |
| { | |
| ea = ea.expressionSemantic(sc); | |
| // must not interpret the args, excepting template parameters | |
| if (ea.op != EXP.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 == EXP.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, EXPtoString(ea.op).ptr, ea.toChars()); | |
| if (ea.op == EXP.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 == EXP.error) | |
| { | |
| err = true; | |
| continue; | |
| } | |
| (*tiargs)[j] = ea; | |
| if (ea.op == EXP.type) | |
| { | |
| ta = ea.type; | |
| goto Ltype; | |
| } | |
| if (ea.op == EXP.scope_) | |
| { | |
| sa = (cast(ScopeExp)ea).sds; | |
| goto Ldsym; | |
| } | |
| if (ea.op == EXP.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 == EXP.dotVariable && !(flags & 1)) | |
| { | |
| // translate expression to dsymbol. | |
| sa = (cast(DotVarExp)ea).var; | |
| goto Ldsym; | |
| } | |
| if (ea.op == EXP.template_) | |
| { | |
| sa = (cast(TemplateExp)ea).td; | |
| goto Ldsym; | |
| } | |
| if (ea.op == EXP.dotTemplateDeclaration && !(flags & 1)) | |
| { | |
| // translate expression to dsymbol. | |
| sa = (cast(DotTemplateExp)ea).td; | |
| goto Ldsym; | |
| } | |
| if (ea.op == EXP.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.initial && 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.initial); | |
| 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.initial) | |
| 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.initial) | |
| { | |
| 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.initial) | |
| { | |
| 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 == EXP.variable) | |
| { | |
| sa = (cast(VarExp)ea).var; | |
| goto Lsa; | |
| } | |
| if (ea.op == EXP.this_) | |
| { | |
| sa = (cast(ThisExp)ea).var; | |
| goto Lsa; | |
| } | |
| if (ea.op == EXP.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 != EXP.int64 && ea.op != EXP.float64 && ea.op != EXP.complex80 && ea.op != EXP.null_ && ea.op != EXP.string_ && ea.op != EXP.arrayLiteral && ea.op != EXP.assocArrayLiteral && ea.op != EXP.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 || dparent.isModule) | |
| 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 | |
| } | |
| } | |
| //https://issues.dlang.org/show_bug.cgi?id=17870 | |
| if (dparent.isClassDeclaration() && enclosing.isClassDeclaration()) | |
| { | |
| auto pc = dparent.isClassDeclaration(); | |
| auto ec = enclosing.isClassDeclaration(); | |
| if (pc.isBaseOf(ec, null)) | |
| goto L1; | |
| else if (ec.isBaseOf(pc, null)) | |
| { | |
| enclosing = dparent; | |
| goto L1; | |
| } | |
| } | |
| 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 | |
| //printf("%s.appendToModuleMember() enclosing = %s mi = %s\n", | |
| // toPrettyChars(), | |
| // enclosing ? enclosing.toPrettyChars() : null, | |
| // mi ? mi.toPrettyChars() : null); | |
| if (global.params.allInst || !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 (needsCodegen()). | |
| */ | |
| 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()) | |
| { | |
| if (mi.importedFrom) | |
| { | |
| mi = mi.importedFrom; | |
| assert(mi.isRoot()); | |
| } | |
| else | |
| { | |
| // This can happen when using the frontend as a library. | |
| // Append it to the non-root module. | |
| } | |
| } | |
| } | |
| 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. | |
| * Unless it is re-appended to a root module later (with changed minst). | |
| */ | |
| } | |
| //printf("\t-. mi = %s\n", mi.toPrettyChars()); | |
| assert(!memberOf || (!memberOf.isRoot() && mi.isRoot()), "can only re-append from non-root to root module"); | |
| 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 == EXP.tuple || e.op == EXP.scope_ || | |
| e.op == EXP.type || e.op == EXP.dotType || | |
| e.op == EXP.template_ || e.op == EXP.dotTemplateDeclaration || | |
| e.op == EXP.function_ || e.op == EXP.error || | |
| e.op == EXP.this_ || e.op == EXP.super_ || | |
| e.op == EXP.dot) | |
| return false; | |
| if (e.op != EXP.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 == EXP.dotVariable) | |
| { | |
| e = (cast(DotVarExp)e).e1; | |
| } | |
| // this.x.y and super.x.y couldn't possibly be valid values. | |
| if (e.op == EXP.this_ || e.op == EXP.super_) | |
| return false; | |
| // e.type.x could be an alias | |
| if (e.op == EXP.dotType) | |
| return false; | |
| // var.x.y is the only other possible form of alias | |
| if (e.op != EXP.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.initial) | |
| { | |
| if (td._scope) | |
| td.dsymbolSemantic(td._scope); | |
| else | |
| { | |
| semanticRun = PASS.initial; | |
| 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. | |
| */ | |
| static if (__VERSION__ < 2099) // https://issues.dlang.org/show_bug.cgi?id=22717 | |
| res = (cast()s.ti).equalsx(cast()ti); | |
| else | |
| res = (cast()ti).equalsx(cast()s.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 == EXP.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 == EXP.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 == EXP.this_ || ea.op == EXP.super_)) | |
| sa = (cast(ThisExp)ea).var; | |
| else if (ea && ea.op == EXP.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); | |
| } | |
| } | |
| extern (C++) 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; | |
| } | |
| } | |
| private void write(ref OutBuffer buf, RootObject obj) | |
| { | |
| if (obj) | |
| { | |
| buf.writestring(obj.toChars()); | |
| } | |
| } |