Skip to content

Commit

Permalink
fix Issue 2803 - template + default argument = doesn't work
Browse files Browse the repository at this point in the history
  • Loading branch information
9rnsr committed Apr 8, 2015
1 parent 9a61591 commit 766e465
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 14 deletions.
124 changes: 110 additions & 14 deletions src/template.c
Expand Up @@ -1359,16 +1359,19 @@ 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);

// 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.
*/
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1485,7 +1488,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(
continue;
goto Lnomatch;
}
Expression *farg = (*fargs)[argi];
farg = (*fargs)[argi];
if (!farg->implicitConvTo(p->type))
goto Lnomatch;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
66 changes: 66 additions & 0 deletions test/runnable/template9.d
Expand Up @@ -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

Expand Down Expand Up @@ -4432,6 +4497,7 @@ int main()
test2296();
bug4984();
test2579();
test2803();
test5886();
test5393();
test5896();
Expand Down

0 comments on commit 766e465

Please sign in to comment.