September 25
https://issues.dlang.org/show_bug.cgi?id=24785

          Issue ID: 24785
           Summary: Add explicit template arguments for lambdas
           Product: D
           Version: D2
          Hardware: x86
                OS: Windows
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody@puremagic.com
          Reporter: qs.il.paperinik@gmail.com

Add explicit template arguments for lambdas

After the `function` and `delegate` keyword, optionally allow for a template
argument list, introduced by the `template` keyword and a following template
parameter list. The parameters of such a lambda would be parsed exactly like a
regular function’s parameter list, i.e. `function template() (x) {}` (unless
`x` is a type in scope) will not work. The explicit write-out of
`function(x){}` is `function template(T)(T x) {}`.

The advantages:
- A lambda can be a template, even if it would otherwise not be: `function
template() int (int x) => x`
- A programmer can use the template type parameters in the lambda body
- Constraints are possible
- The ordering of type parameters is up to the programmer: `function
template(T1, T2) (T2 x, T1 y) { }`
- Variadic lambdas are possible: `function template(Ts...) (Ts args) { }`
- Template parameters can be any kind of template parameter, not just type.
- Template parameters can have a specialization.
- Template parameters can have a default.

Not really an argument: C++20 is ahead of D in this regard.

Required grammar changes:

```diff
    FunctionLiteral:
        function RefOrAutoRef? BasicTypeWithSuffixes? ParameterWithAttributes?
FunctionLiteralBody
        delegate RefOrAutoRef? BasicTypeWithSuffixes?
ParameterWithMemberAttributes? FunctionLiteralBody
+       function template TemplateParameters RefOrAutoRef?
BasicTypeWithSuffixes? ParameterWithAttributes? Constraint? FunctionLiteralBody
+       delegate template TemplateParameters RefOrAutoRef?
BasicTypeWithSuffixes? ParameterWithMemberAttributes? Constraint?
FunctionLiteralBody
```

A fully formed function literal can look like this:
```d
delegate template(T : Object = Object, Ts...)
auto ref T (auto ref Ts args) const @safe
if (allSatisfy!(function template(T) => is(T : Object), Ts))
in (Ts.length == 0 || args[$-1] !is null)
{ ... }
```

-------------------------------

I tried implementing this, and the parsing is doable, but it seems the
semantics doesn’t see the template parameters. A simple `function template(T)
=> is(T)` sets `is(T)` always `false`.

For a quick start for anyone who wants to tackle this, this is the diff of parse.d I used: It works perfectly when using `alias fp = function template(T) => is(T);`, however, that’s due to `alias` lowering stuff to immediate declarations.

diff --git a/compiler/src/dmd/parse.d b/compiler/src/dmd/parse.d index bb2411825f..e4092f6040 100644
--- a/compiler/src/dmd/parse.d
+++ b/compiler/src/dmd/parse.d
@@ -5078,7 +5078,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
     private AST.Dsymbol parseFunctionLiteral()
     {
         const loc = token.loc;
+        bool isExplicitTemplate = false;
         AST.TemplateParameters* tpl = null;
+        AST.Expression constraint = null;
         AST.ParameterList parameterList;
         AST.Type tret = null;
         StorageClass stc = 0;
@@ -5090,6 +5092,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         case TOK.delegate_:
             save = token.value;
             nextToken();
+
+            if (token.value == TOK.template_)
+            {
+                nextToken();
+                isExplicitTemplate = true;
+                tpl = parseTemplateParameterList();
+            }
+
             if (token.value == TOK.auto_)
             {
                 nextToken();
@@ -5158,7 +5168,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             {
                 // (parameters) => expression
                 // (parameters) { statements... }
-                parameterList = parseParameterList(&tpl);
+                parameterList = parseParameterList(isExplicitTemplate ? null :
&tpl);
                 stc = parsePostfix(stc, null);
                 if (StorageClass modStc = stc & STC.TYPECTOR)
                 {
@@ -5196,6 +5206,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
             assert(0);
         }

+        if (isExplicitTemplate)
+        {
+            constraint = parseConstraint();
+        }
+
         auto tf = new AST.TypeFunction(parameterList, tret, linkage, stc);
         tf = cast(AST.TypeFunction)tf.addSTC(stc);
         auto fd = new AST.FuncLiteralDeclaration(loc, Loc.initial, tf, save,
null, null, stc & STC.auto_);
@@ -5215,7 +5230,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
         }
         else
         {
-            parseContracts(fd);
+            parseContracts(fd, isExplicitTemplate);
         }

         if (tpl)

--