diff --git a/src/template.c b/src/template.c index 5d610222e29f..34a2f363f891 100644 --- a/src/template.c +++ b/src/template.c @@ -1359,9 +1359,10 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( // Loop through the function parameters { - //printf("%s nfargs=%d, nfparams=%d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple->objects.dim : 0); + //printf("%s\n\tnfargs = %d, nfparams = %d, tuple_dim = %d\n", toChars(), nfargs, nfparams, declaredTuple ? declaredTuple->objects.dim : 0); //printf("\ttp = %p, fptupindex = %d, found = %d, declaredTuple = %s\n", tp, fptupindex, fptupindex != IDX_NOTFOUND, declaredTuple ? declaredTuple->toChars() : NULL); size_t argi = 0; + size_t nfargs2 = nfargs; // nfargs + supplied defaultArgs for (size_t parami = 0; parami < nfparams; parami++) { Parameter *fparam = Parameter::getNth(fparameters, parami); @@ -1369,6 +1370,8 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( // 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. */ @@ -1402,12 +1405,12 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( } } - if (nfargs - argi < rem) + if (nfargs2 - argi < rem) goto Lnomatch; - declaredTuple->objects.setDim(nfargs - argi - rem); + declaredTuple->objects.setDim(nfargs2 - argi - rem); for (size_t i = 0; i < declaredTuple->objects.dim; i++) { - Expression *farg = (*fargs)[argi + i]; + farg = (*fargs)[argi + i]; // Check invalid arguments to detect errors early. if (farg->op == TOKerror || farg->type->ty == Terror) @@ -1485,7 +1488,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( continue; goto Lnomatch; } - Expression *farg = (*fargs)[argi]; + farg = (*fargs)[argi]; if (!farg->implicitConvTo(p->type)) goto Lnomatch; } @@ -1495,18 +1498,111 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( if (argi >= nfargs) // if not enough arguments { - if (fparam->defaultArg) + if (!fparam->defaultArg) + goto Lnomatch; + + /* Bugzilla 2803: Before the starting of type deduction from the function + * default arguments, set the already deduced parameters into paramscope. + * It's necessary to avoid breaking existing acceptable code. Cases: + * + * 1. Already deduced template parameters can appear in fparam->defaultArg: + * auto foo(A, B)(A a, B b = A.stringof); + * foo(1); + * // at fparam == 'B b = A.string', A is equivalent with the deduced type 'int' + * + * 2. If prmtype depends on default-specified template parameter, the + * default type should be preferred. + * auto foo(N = size_t, R)(R r, N start = 0) + * foo([1,2,3]); + * // at fparam `N start = 0`, N should be 'size_t' before + * // the deduction result from fparam->defaultArg. + */ + if (argi == nfargs) { - /* Default arguments do not participate in template argument - * deduction. - */ - goto Lmatch; + for (size_t i = 0; i < dedtypes->dim; i++) + { + Type *at = isType((*dedtypes)[i]); + if (at && at->ty == Tnone) + { + TypeDeduced *xt = (TypeDeduced *)at; + (*dedtypes)[i] = xt->tded; // 'unbox' + delete xt; + } + } + for (size_t i = ntargs; i < dedargs->dim; i++) + { + TemplateParameter *tparam = (*parameters)[i]; + + RootObject *oarg = (*dedargs)[i]; + RootObject *oded = (*dedtypes)[i]; + if (!oarg) + { + if (oded) + { + if (tparam->specialization() || !tparam->isTemplateTypeParameter()) + { + /* The specialization can work as long as afterwards + * the oded == oarg + */ + (*dedargs)[i] = oded; + MATCH m2 = tparam->matchArg(loc, paramscope, dedargs, i, parameters, dedtypes, NULL); + //printf("m2 = %d\n", m2); + if (m2 <= MATCHnomatch) + goto Lnomatch; + if (m2 < matchTiargs) + matchTiargs = m2; // pick worst match + if (!(*dedtypes)[i]->equals(oded)) + error("specialization not allowed for deduced parameter %s", tparam->ident->toChars()); + } + else + { + if (MATCHconvert < matchTiargs) + matchTiargs = MATCHconvert; + } + (*dedargs)[i] = declareParameter(paramscope, tparam, oded); + } + else + { + oded = tparam->defaultArg(loc, paramscope); + if (oded) + (*dedargs)[i] = declareParameter(paramscope, tparam, oded); + } + } + } } + nfargs2 = argi + 1; + + /* If prmtype does not depend on any template parameters: + * + * auto foo(T)(T v, double x = 0); + * foo("str"); + * // at fparam == 'double x = 0' + * + * or, if all template parameters in the prmtype are already deduced: + * + * auto foo(R)(R range, ElementType!R sum = 0); + * foo([1,2,3]); + * // at fparam == 'ElementType!R sum = 0' + * + * Deducing prmtype from fparam->defaultArg is not necessary. + */ + if (prmtype->deco || + prmtype->syntaxCopy()->trySemantic(loc, paramscope)) + { + ++argi; + continue; + } + + // Deduce prmtype from the defaultArg. + farg = fparam->defaultArg->syntaxCopy(); + farg = farg->semantic(paramscope); + farg = resolveProperties(paramscope, farg); } else { - Expression *farg = (*fargs)[argi]; - + farg = (*fargs)[argi]; + } + { // Check invalid arguments to detect errors early. if (farg->op == TOKerror || farg->type->ty == Terror) goto Lnomatch; @@ -1766,8 +1862,8 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( } ++argi; } - //printf("-> argi = %d, nfargs = %d\n", argi, nfargs); - if (argi != nfargs && !fvarargs) + //printf("-> argi = %d, nfargs = %d, nfargs2 = %d\n", argi, nfargs, nfargs2); + if (argi != nfargs2 && !fvarargs) goto Lnomatch; } diff --git a/test/runnable/template9.d b/test/runnable/template9.d index 94a05ebc037b..7c5a998d45d0 100644 --- a/test/runnable/template9.d +++ b/test/runnable/template9.d @@ -456,6 +456,71 @@ void test2579() foo2579( (in Object o) { return 15; } ); } +/**********************************/ +// 2803 + +auto foo2803(T)(T t = 0) { return t; } + +struct S2803 {} +S2803 s2803; +ref S2803 getS2803() { return s2803; } +auto fun2803(T, U)(T t, ref U u = getS2803) +{ + static assert(is(U == S2803)); + return &u; +} + +// from the past version of std.conv +template to2803(T) { T to2803(S)(S src) { return T.init; } } +auto toImpl2803a(T, S)(S s, in T left, in T sep = ", ", in T right = "]") {} +auto toImpl2803b(T, S)(S s, in T left = to2803!T(S.stringof~"("), in T right = ")") {} +auto toImpl2803c(T, S)(S s, in T left = S.stringof~"(" , in T right = ")") {} // combination with enh 13944 + +// from std.range.package in 2.067a. +auto enumerate2803(En = size_t, R)(R r, En start = 0) +{ + // The type of 'start' should be size_t, it's the defaultArg of En, + // rather than the deduced type from its defualtArg '0'. + static assert(is(typeof(start) == size_t)); + return start; +} + +// from std.numeric. +alias ElementType2803(R) = typeof(R.init[0].init); +void normalize2803(R)(R range, ElementType2803!R sum = 1) +{ + // The type of 'sum' should be determined to ElementType!(double[]) == double + // before the type deduction from its defaultArg '1'. + static assert(is(typeof(sum) == double)); +} + +void test2803() +{ + assert(foo2803() == 0); + assert(foo2803(1) == 1); + + S2803 s; + assert(fun2803(1) is &s2803); + assert(fun2803(1, s) is &s); + + // regression cases + + toImpl2803a!string(1, "["); + + toImpl2803b! string(1); + toImpl2803b!wstring(1); + toImpl2803b!dstring(1); + + toImpl2803c! string(1); + toImpl2803c!wstring(1); // requires enhancement 13944 + toImpl2803c!dstring(1); // requires enhancement 13944 + + enumerate2803([1]); + + double[] a = []; + normalize2803(a); +} + /**********************************/ // 4953 @@ -4432,6 +4497,7 @@ int main() test2296(); bug4984(); test2579(); + test2803(); test5886(); test5393(); test5896();