Comment #0 by qs.il.paperinik — 2024-09-25T16:38:03Z
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)
Comment #1 by qs.il.paperinik — 2024-09-25T16:41:17Z
This would enable things like:
```d
allSatisfy!(function template(T) => is(T : Object), ...)
```
Although requiring some modifications to `allSatisfy` to make it call a function pointer or delegate with zero arguments if given one via `alias`.
Comment #2 by robert.schadek — 2024-12-13T19:37:37Z