I was writing a program recently where I was passing around deeply nested delegates, so I wrote a template alias to represent the type. (Without it, it was nearly impossible to use the correct amount of nested delegates.)
My program uses IFTI heavily, and what I found out was that as soon as I implemented the template aliasing, my IFTI didn't work anymore.
After a bit of thinking, I realized that, in general, IFTI can't dive into templates and speculate about what they might be, since each template instance could represent an infinite number of types. It's an impossible search. But in my special case, the search is linear, and can easily be accomplished.
My request: when a template function uses some type-expression which includes an instantiation of a template, and there is only one version of that template, and that template just has a single alias (or typedef, maybe?), then replace the template reference with the alias, and use that to compute IFTI.
EXAMPLE CODE:
template wrap(T)
{
alias void delegate(T*) wrap;
}
alias void delegate(int*) wrap_int;
void Foo1(T)(void delegate(T*)) {}
void Foo2(T)(wrap!(T)) {}
void Foo3 (wrap_int) {}
void delegate(T*) Bar1(T)( T arg) { return null; }
wrap!(T) Bar2(T)( T arg) { return null; }
wrap_int Bar3 (int arg) { return null; }
void main()
{
int i;
Foo1(Bar1(i));
Foo1(Bar2(i));
Foo1(Bar3(i));
// these three have IFTI failures because the only way
// figure out the parameter T for template function Foo2
// is to realize that wrap!(T) is a template alias, and
// it seems that DMD doesn't realize that.
//
// Foo2(Bar1(i));
// Foo2(Bar2(i));
// Foo2(Bar3(i));
Foo3(Bar1(i));
Foo3(Bar2(i));
Foo3(Bar3(i));
}
END EXAMPLE CODE
My analysis: IFTI works quite well with delegates (see Foo1). IFTI also works quite well with aliases (DMD looks right through the alias, see Foo3). IFTI doesn't work when the alias is inside a template. What I'm suggesting is that DMD should look at the Foo2 function and realize that the type
wrap!(T)
while a template expression, will always be equivalent to
void delegate(T*)
due to the fact that:
1) There is only declartion of the template
2) The declaration only includes a single alias
Once DMD knows that Foo2 is actually
void Foo2(T)(void delegate(T*)) {}
IFTI should work quite nicely.
Also note, BTW, that this transformation should happen on any number of expressions in the function declaration, it should recurse, and it should be OK if the template parameter is a tuple. See my background code below.
BACKGROUND - A SNIPPET OF THE ACTUAL CODE WHERE I FOUND THIS PROBLEM:
template RegisterFor_mailbox(TPL...)
{
alias void delegate(TPL) RegisterFor_mailbox;
}
template RegisterFor_retval(TPL...)
{
alias void delegate(RegisterFor_mailbox!(TPL)) RegisterFor_retval;
}
RegisterFor_retval!()
RegisterFor_char()
(Registry reg,char c)
{
...
}
// this function supports chaining of registrations, in this case chaining an
// IDENTIFIER to a pre-existing chain
RegisterFor_retval!(IDENTIFIER,TPL)
RegisterFor_IDENTIFIER(TPL...)
(Registry reg,
RegisterFor_retval!(TPL) chain)
{
...
}
void main()
{
Registry reg = <whatever>;
// as explained below, I currently have to explicitly instantiate these
// function calls. I'd like IFTI to do it for me, as I show here:
RegisterFor_char (reg, '{', // my code also has chain-to-char
RegisterFor_IDENTIFIER(reg,
RegisterFor_IDENTIFIER(reg,
RegisterFor_char (reg, '}'))))
(delegate void(IDENTIFIER id1,IDENTIFIER id2)
{
<mailbox code resides here>
});
}
// in a chain, the deepest element (the last in the chain) should be easy to
// IFTI; the return value from that is one of the args for the next-deepest,
// and if IFTI worked the way I hoped, then that would allow us to IFTI the
// next-deepest, and so on up the chain.
END BACKGROUND CODE
You can see from the above that if I had to replace all of my nice RegisterFor_retval!(...) expressions with the actual delegate code, the complexity shoots through the roof. (I have even worse places in my code. In one place, I have a paramter to a function which is function-pointer-taking-delegate-argument-returning-delegate-nested-3-deep-deepest-delegate-taking-pointer-to-struct-argument.)
It's very hard to live without these aliases. So right now, I am forcing the calling code specifically choose the template parameters. That's ugly.
Comment #1 by bugzilla — 2008-02-21T01:02:52Z
This enhancement request is a special case of issue 1454, which is marked as invalid. I agree with Russ that the general case is intractable, but this special case might be doable, so it's a good idea for an enhancement request.
Comment #2 by shro8822 — 2008-07-02T16:10:00Z
BUMP
Another cases where this would be handy
public template Foo(T: char) {alias Foo_dwc!(T) Foo; }
public template Foo(T: wchar) {alias Foo_dwc!(T) Foo; }
public template Foo(T: dchar) {alias Foo_dwc!(T) Foo; }
private void Foo_dwc(T)(T arg) { }
void main(char[][] args)
{
Foo('a');
}
Comment #3 by schveiguy — 2008-11-14T14:00:29Z
*** Bug 1653 has been marked as a duplicate of this bug. ***
Comment #4 by schveiguy — 2010-03-09T04:15:46Z
*** Issue 3904 has been marked as a duplicate of this issue. ***
Comment #5 by timon.gehr — 2012-02-17T14:20:38Z
*** Issue 7529 has been marked as a duplicate of this issue. ***
Comment #6 by code — 2012-02-18T09:40:40Z
One issue with this is that treating certain kinds of templates
different that others will make it very difficult to understand
why matching might fail.
If we want to have alias templates to behave different than plain
templates they should have a language construct that enforces the
limitations and resembles struct templates.
alias(T) void delegate(T*) wrap;
expands to:
template wrap(T) { alias void delegate(T*) wrap; }
Comment #7 by ag0aep6g — 2018-04-17T16:16:46Z
*** Issue 10884 has been marked as a duplicate of this issue. ***
Comment #8 by razvan.nitu1305 — 2019-10-11T09:34:38Z
*** Issue 1942 has been marked as a duplicate of this issue. ***
Comment #11 by john.michael.hall — 2022-10-11T21:31:16Z
Creating a new type with an alias this'ed parameter is similar conceptually to a template alias. The new type is implicitly convertible to the target of the alias this and can be used wherever that original type was used. However, since it is a separate type, it can't called called where the alias is used.
```d
struct TemplateType(T) {}
//alias TemplateAlias(T) = TemplateType!T;
struct TemplateAlias(T) {
TemplateType!T x;
alias x this;
}
void templateFunction(T)(TemplateType!T arg) {}
void templateAliasFunction(T)(TemplateAlias!T arg) {}
void main()
{
TemplateType!int inst;
TemplateAlias!int ainst;
templateFunction(inst);
templateFunction(ainst);
templateAliasFunction(inst); //doesn't compile
templateAliasFunction(ainst);
}
```
Perhaps what is needed is some kind of mechanism like alias this...not in the sense of implicit conversion but in terms the programmer having the tools to tell the compiler what to do.
The idea would be that your template alias would include an additional template (with some rudimentary sketch below), call it `opResolveAlias`, that would let the compiler know the relationships going in reverse (so instead of from the alias to the alias target, this would be going from the alias target to the alias). The compiler could then make use of that information to make sure the function call can properly occur (with type inference as needed).
So for instance, if you have a function that accepts a `TemplateAlias!T` parameter, but pass in a `TemplateType!int` parameter, then currently the compiler doesn't look through the `TemplateAlias!T` to see that `TemplateType!int` is the same. However, if the compiler knows that a `TemplateType!int` is the same as `TemplateAlias!int`, then it would be better able to proceed.
```d
template TemplateAlias(T) {
alias TemplateAlias = TemplateType!T;
template opResolveAlias(alias U)
if(is(U == TemplateType!V, V))
{
alias opResolveAlias = TemplateAlias!V;
}
}
```
For simple cases like this, the compiler could generate its own `opResolveAlias`, but in the more general case it would be up to the user to provide it if it is important to them.
Issue 16486 has an example of a more complicated template alias [1] that should still be able to have this approach applied:
```d
template TestAlias(T)
{
static if (is(T == float)) alias TestAlias = TestType!int;
else alias TestAlias = string;
// new part:
template opResolveAlias(alias U)
{
static if (is(U == TestType!int)) {
alias opResolveAlias = TestAlias!float;
} else static if (is(U == string)) {
alias opResolveAlias = TestAlias!int; //could put anything there that's not float...not the most elegant
}
}
}
```
[1] https://issues.dlang.org/show_bug.cgi?id=16486#c1
Comment #12 by default_357-line — 2023-03-22T13:03:39Z
*** Issue 23798 has been marked as a duplicate of this issue. ***
Comment #13 by robert.schadek — 2024-12-13T17:48:11Z