diff --git a/changelog/default_after_variadic.dd b/changelog/default_after_variadic.dd new file mode 100644 index 000000000000..4aff6f044099 --- /dev/null +++ b/changelog/default_after_variadic.dd @@ -0,0 +1,17 @@ +Function parameters with default values are now allowed after variadic template parameters, and always take their default values. This allows using special tokens (eg __FILE__) after variadic parameters, which was previously impossible + +Eg: +--- +string log(T...)(T a, string file = __FILE__, int line = __LINE__) +{ + return text(file, ":", line, " ", a); +} + +assert(log(10, "abc") == text(__FILE__, ":", __LINE__, " 10abc")); +--- + +This should be preferred to the previous workaround, which caused template bloat: +--- +string log(string file = __FILE__, int line = __LINE__, T...)(T a); +--- + diff --git a/src/dmd/dtemplate.d b/src/dmd/dtemplate.d index 28de9558ceba..55a567d7d350 100644 --- a/src/dmd/dtemplate.d +++ b/src/dmd/dtemplate.d @@ -1342,13 +1342,17 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol declaredTuple = new Tuple(); (*dedargs)[parameters.dim - 1] = declaredTuple; - /* Count function parameters following a tuple parameter. - * void foo(U, T...)(int y, T, U, int) {} // rem == 2 (U, int) + /* 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 = Parameter.getNth(fparameters, j); + if(p.defaultArg) + { + break; + } if (!reliesOnTident(p.type, parameters, inferStart)) { Type pt = p.type.syntaxCopy().typeSemantic(fd.loc, paramscope); diff --git a/test/runnable/testdefault_after_variadic.d b/test/runnable/testdefault_after_variadic.d new file mode 100644 index 000000000000..4aa140002c31 --- /dev/null +++ b/test/runnable/testdefault_after_variadic.d @@ -0,0 +1,62 @@ +/* +PERMUTE_ARGS: +*/ + +import std.typecons : tuple; +import std.conv : text; + +void fun0(U, T...)(U gold, int b_gold, T a, int b) +{ + assert(tuple(a) == gold); + assert(b == b_gold); +} + +void fun(U, T...)(U gold, T a, int b = 1) +{ + assert(tuple(a) == gold); + assert(b == 1); +} + +void fun2(U, V, T...)(U gold, V gold2, T a, string file = __FILE__, int line = __LINE__) +{ + assert(tuple(a) == gold); + assert(tuple(file, line) == gold2); +} + +// +void fun3(int[] gold, int[] a...) +{ + assert(gold == a); +} + +/+ +NOTE: this is disallowed by the parser: + +void fun4(int[] gold, int[] a ..., int b = 1) +{ + assert(gold==a); + assert(b==1); +} ++/ + +// Example in changelog +string log(T...)(T a, string file = __FILE__, int line = __LINE__) +{ + return text(file, ":", line, " ", a); +} + +void main() +{ + fun0(tuple(10), 7, 10, 7); + + fun(tuple()); + fun(tuple(10), 10); + fun(tuple(10, 11), 10, 11); + + fun2(tuple(10), tuple(__FILE__, __LINE__), 10); + + fun3([1, 2, 3], 1, 2, 3); + // fun4([1,2,3], 1,2,3); + + assert(log(10, "abc") == text(__FILE__, ":", __LINE__, " 10abc")); +}