Does `is` expression with template alias need fixing.

FeepingCreature feepingcreature at gmail.com
Fri Mar 24 09:30:30 UTC 2023


On Thursday, 23 March 2023 at 12:40:47 UTC, kdevel wrote:
> On Thursday, 23 March 2023 at 00:36:12 UTC, Elfstone wrote:
>> Yes, I can.
>>
>> ```C++
>> template <typename T>
>> using Vector3 = Matrix<T, 3, 1>;
>>
>> template <typename T>
>> void foo(const Vector3<T>& v) {
>> }
>>
>> int main() {
>> 	foo(Vector3<float>());
>> }
>> ```
>> I can declare parameters with Vector3\<T\>, and it gets me the 
>> right T, which is all I need.
>>
>> D allows me to declare template alias parameter but it matches 
>> nothing at all. Then why allow people to write template alias 
>> parameters at all?
>
> It is interesting that this
>
> ```
> struct B (T, V) { }
>
> alias A = B!(double, void);
>
> void f (A) { }
>
> void main ()
> {
>    A a;
>    f (a);
>    B!(double, void) b;
>    f (b); // works
> }
> ```
>
> compiles while the version with incomplete specialization 
> requires a version of `f` not using the alias template in the 
> parameter list:
>
> ```
> struct B (T, V) { }
>
> alias A (T) = B!(T, void);
>
> // void f (T) (A!(T)) { } // fail
> void f (T) (B!(T, void)) { } // cannot use A!T here, why not?
>
> void main ()
> {
>    A!double a;
>    f (a); // does not call void f (T) (A!(T))
>    B!(double, void) b;
>    f (b); // does not call void f (T) (A!(T))
> }
> ```
>
> In the failing case dmd says
>
> ```
> vm.d(11): Error: none of the overloads of template `vm.f` are 
> callable using argument types `!()(B!(double, void))`
> vm.d(5):        Candidate is: `f(T)(A!T)`
> vm.d(13): Error: none of the overloads of template `vm.f` are 
> callable using argument types `!()(B!(double, void))`
> vm.d(5):        Candidate is: `f(T)(A!T)`
> ```
>
> It seems that dmd views `A!double` and `B!(double, void)` as 
> different types which they aren't. Isn't there an 
> alias-expansion phase during compilation?
>
> [1] https://issues.dlang.org/show_bug.cgi?id=23798

You're misunderstanding the problem.

When you call a template function, DMD (and C++) performs IFTI, 
implicit function template instantiation. This requires 
*reverse-engineering* a type T that fulfills the requirement for 
the function call. For `void f(T)(A!T)`, DMD has to unify the 
given *parameter type* with the abstract type expression `A!T` 
and figure out a matching `T`.

If `A` is a struct, it can do this because for a struct type, DMD 
*knows which template instance it came from* and can just look up 
the parameter type that was used to create it. It looks it up, to 
be clear, *in the caller's type*. But an alias is not itself a 
type! So in the case where `A` is an alias, C++ and DMD have to 
do the *opposite* and figure out that `A` - **not** the type 
`A!T`, because we don't know `T` yet, but the `A` from the 
*syntax node* `A!T` in the *callee!* - is an alias template  with 
a trivial expansion, so that *for the purpose of type inference 
only*, the abstract expression `A!T` can be treated as equivalent 
to `B!(T, void)` - still without having any concrete `T`, just a 
syntax node `T`.

So in C++, it's just as if you wrote `void f(T)(B!(T, void)) {}`, 
because it substitutes `A!T`, *sight unseen*, in the *called 
function's parameter list*.

That's why this works:

```
template <typename T, int M, int N>
struct Matrix {
};

template <typename T>
using Vector3 = Matrix<T, 3, 1>;

/**Exactly equivalent to template<typename T>
void foo(Vector<T> vector) {}*/
template<typename T>
void foo(Matrix<T, 3, 1> vector) {}

int main() {
   foo(Vector3<int>());
}
```


D does not do this yet.


More information about the Digitalmars-d mailing list