Jump to page: 1 2 3
Thread overview
Trying to do alias template deduction myself.
Mar 31, 2023
Elfstone
Mar 31, 2023
jmh530
Apr 03, 2023
Elfstone
Apr 03, 2023
Salih Dincer
Apr 03, 2023
Salih Dincer
Apr 04, 2023
Elfstone
Apr 04, 2023
Elfstone
Apr 05, 2023
Elfstone
Apr 06, 2023
Elfstone
Apr 07, 2023
Elfstone
Apr 07, 2023
Salih Dincer
Apr 07, 2023
Elfstone
May 18
jmh530
May 22
Elfstone
May 23
Elfstone
May 24
jmh530
March 31, 2023

Related thread: https://forum.dlang.org/thread/wgvtwckvjyhnbpcuyyqy@forum.dlang.org

It seems dtemplate.deduceType does all the deduction for both is and IFTI. I can already add a better error message, right before "goto Lnomatch":
cannot deduce the instantiation of an alias template (is that the right term?)
This will better explain to new users why it really fails:
NOT because no callable candidate exists, but because D never tried to do anything about an alias template to find the match. So try instantiating the function explicitly then it will work.

But my goal is:

struct Matrix(U, size_t M, size_t N)
{
    U[M * N] data;
}

alias Vector3(U) = Matrix!(U, 3, 1);
alias Vec3(U) = Vector3!U;
alias Vec3Alt = Vector3;
alias Vec3Var(U...) = Vector3!(U);
alias V3(U) = Vector3!U;

alias Identity(U) = int;

void foo1(U)(in Vector3!U a)
{
}

void foo2(U)(in Vec3!U v)
{
}

void foo2Alt(U)(in Vec3Alt!U v)
{
}

void foo3Var(U...)(Vec3Var!(U))
{
}

void foo3(U)(in V3!U)
{
}

void foo4(U)(Identity!U u)
{
}

void main()
{
    auto instVar = Vector3!float();
    // The current behaviour is: deduction for alias template will always fail.
    // foo1(instVar); // expect OK
    // foo2(instVar); // expect OK
    // foo2Alt(instVar); // expect OK
    // foo3Var(instVar); // expect OK
    // foo3(instVar); // expect OK

    // foo4(Identity!int()); // let it fail

    // import std.regex;
    // import std.traits;
    // static assert(isInstanceOf!(Regex, Regex!char)); // now fails, not sure it can be fixed along
}

How I like it to behave is based on what C++ offers.

template <typename T, size_t M, size_t N>
struct Matrix {
	T data[M * N];
};

template<typename T, size_t M, size_t N>
using Mat = Matrix<T, M, N>;

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

template <typename T>
using Vec3 = Vector3<T>;

template <typename T>
using Identity = int;

template <typename T>
void foo1(const Vector3<T>& v) {

}

template <typename T>
void foo2(const Vec3<T>& v) {

}

template <typename T, size_t M, size_t N>
void foo3(const Mat<T, M, N>& v) {

}

template <typename T>
void foo4(const Identity<T> id) {

}

int main() {
	foo1(Vector3<float>()); // OK
	foo2(Vector3<float>()); // OK
	foo3(Vector3<float>()); // OK

	// Let it fail
	// foo4(Identity<int>()); // no matching overloaded function found.
}

My current idea is to "expand" the alias with its to-be-matched parameter(s) to the fullest form if possible (for example, V3!U > Vec3!U > Matrix!(U, 3, 1)), then go to the parameter matching routine.

It seems possible, if I just follow TemplateDeclaration.onemember and expand the expression level by level, give up when running into overloads, conditions, or anything too complicate (I'm not sure if onemember ensures no conditions). I could be wrong, but based on my few hours of reading the code and really helpful comments in the forum, I believe people who are more familiar with the compiler code can implement it instantantly.

I'm making this post not to promise to fix it (I'll try), but to gather opinions:
Do you think such behaviour in D is desirable (regardless of how I imagine it can be implemented)?
What do I miss?

March 31, 2023

On Friday, 31 March 2023 at 05:19:48 UTC, Elfstone wrote:

>

[..]
Do you think such behaviour in D is desirable (regardless of how I imagine it can be implemented)?

YES! Thank you for driving this initiative!

March 31, 2023

On Friday, 31 March 2023 at 07:50:58 UTC, Petar Kirov [ZombineDev] wrote:

>

On Friday, 31 March 2023 at 05:19:48 UTC, Elfstone wrote:

>

[..]
Do you think such behaviour in D is desirable (regardless of how I imagine it can be implemented)?

YES! Thank you for driving this initiative!

Ditto.

April 03, 2023

On Friday, 31 March 2023 at 05:19:48 UTC, Elfstone wrote:

>

[..]

Some updates and thoughts.

The good new is, I have all the above cases working (except the one with tuple)! And even more complicated cases, such as:

private struct SomeTemp(T, U, size_t N)
{
}

alias SomeAlias(X, Y, size_t C) = SomeTemp!(Y, X, C);

alias SomeOtherAlias(X, Y) = SomeAlias!(X, Y, 1);

void someOtherFoo(P, Q)(SomeOtherAlias!(Q, P) v)
{
    static assert(is(P == int));
    static assert(is(Q == float));
}

void main()
{
	someOtherFoo(SomeTemp!(float, int 1)()); // ok
	someOtherFoo(SomeTemp!(float, int, 3)()); // fails.
}

As I dig more into the compiler code, it's getting more obvious that this task is much tougher than I previously imagined. I cannot just "expand" the alias expression. It seems the "expansion" happens only during templateInstanceSemantic and I cannot reuse any of that with to-be-deduced arguments.

Now I take another approach: while matching [U_, N_], Vec!(U_, N_) to Matrix!(float, 3, 1), I find the AliasDeclaration (alias Vec(U, N) = Matrix!(U, N, 1)) and call deduceType on [U, N], Matrix!(U, N, 1) - now it can produce [float, 3]. All I need to do is match [float, 3] to [U_, N_]. This also works recursively on a chain of aliases as long as it ends with a template declaration.

The potentional is likely the capability of original IFTI for alias template, but unfortunately I cannot reuse the existing matching code either (before I fully understand it, if ever). Still I figure it's better to write some simplistic matching with limit capability than writing a separate "expansion". It only takes several lines to do the basic matching anyway - the compiler will make sure the arguments really match later.

I'm almost sure my code works with bare type identifiers and values. I can probably support tuple soon. I'll figure out what I can do with fancy U:U*, U: A!U, etc.

I'll upload my code, when I feel more ... secure about the implementation, and because I coded during lunch break, I can't just upload it from work, maybe on weekends.

April 03, 2023

On 4/3/23 8:56 AM, Elfstone wrote:

>

On Friday, 31 March 2023 at 05:19:48 UTC, Elfstone wrote:

>

[..]

Some updates and thoughts.

The good new is, I have all the above cases working (except the one with tuple)!

Just want to say, I'm super excited to see this possibly getting solved!

Thanks for the attempt, and I hope you succeed.

-Steve

April 03, 2023

On Monday, 3 April 2023 at 12:56:10 UTC, Elfstone wrote:

>

I'm almost sure my code works with bare type identifiers and values. I can probably support tuple soon. I'll figure out what I can do with fancy U:U*, U: A!U, etc.

Why don't you try using AliasSeq? I think it would be very elegant with a triple staticMap. For example:

import std.meta;

struct Matrix(U, size_t M, size_t N)
{
    U[M * N] data;
}

alias P = AliasSeq!(byte, short, int);
alias X = AliasSeq!(3, 3, 4);
alias Y = AliasSeq!(1, 3, 2);

void main()
{
  alias Vec3(U) = Matrix!(U, 3, 3);
  alias T = float;

  Vec3!T cube;
  void singleMatrix(U)(Vec3!U v){}

  singleMatrix!T(cube); // ok
  assert(is(typeof(cube) == Vec3!T));

  // ...

  alias Vec3Var(U...) = staticMapTrio!(Matrix, U);
  alias S = AliasSeq!(P, X, Y);

  Vec3Var!S trio;
  void triMatrix(U...)(in Vec3Var!(U) v){}

  triMatrix!S(trio); // ok
  assert(is(typeof(trio) == Vec3Var!S));
}

template staticMapTrio(alias fun, args...) {
  alias staticMapTrio = AliasSeq!();
  static foreach (i; 0..args.length / 3) {
    staticMapTrio = AliasSeq!(staticMapTrio,
      fun!(args[0..$/3][i],
           args[$/3..($/3) * 2][i],
           args[($/3) * 2..($/3) * 3][i]
      )
    );
  }
}
April 03, 2023

On Monday, 3 April 2023 at 19:50:14 UTC, Salih Dincer wrote:

>

... very elegant ...

I like it better when program prints:

> import std.stdio;
import std.meta;

struct Matrix(U, size_t M, size_t N)
{
    U[M * N] data;
}

alias P = AliasSeq!(byte, short, int);
alias X = AliasSeq!(3, 3, 4);
alias Y = AliasSeq!(1, 3, 2);

void main()
{
  alias Vec3(U) = Matrix!(U, 3, 3);
  alias T = float;

  Vec3!T cube;
  void singleMatrix(U)(in Vec3!U v)
  {
    typeid(aTypeOf!(v.data)).write(": ");
    v.data.writeln;
  }

  singleMatrix!T(cube); // ok
  assert(is(typeof(cube) == Vec3!T));

  // ...

  alias Vec3Var(U...) = staticMapTrio!(Matrix, U);
  alias S = AliasSeq!(P, X, Y);

  Vec3Var!S trio;
  void triMatrix(U...)(in Vec3Var!(U) vps)
  {
    foreach(ref v; vps)
    {
      typeid(aTypeOf!(v.data)).write(": ");
      v.data.writeln;
    }
  }

  triMatrix!S(trio); // ok
  assert(is(typeof(trio) == Vec3Var!S));
}

template staticMapTrio(alias fun, args...) {
  alias staticMapTrio = AliasSeq!();
  static foreach (i; 0..args.length / 3) {
    staticMapTrio = AliasSeq!(staticMapTrio,
      fun!(args[0..$/3][i],
           args[$/3..($/3) * 2][i],
           args[($/3) * 2..($/3) * 3][i]
      )
    );
  }
}

template aTypeOf(alias A) {
  alias Type(T : T[]) = T;
  alias aTypeOf = Type!(typeof(A));
}

/* PRINTS:
float: [nan, nan, nan, nan, nan, nan, nan, nan, nan]
byte: [0, 0, 0]
short: [0, 0, 0, 0, 0, 0, 0, 0, 0]
int: [0, 0, 0, 0, 0, 0, 0, 0]
*/

SDB@79

April 04, 2023

On Monday, 3 April 2023 at 19:50:14 UTC, Salih Dincer wrote:

>

On Monday, 3 April 2023 at 12:56:10 UTC, Elfstone wrote:

>

[...]

Why don't you try using AliasSeq? I think it would be very elegant with a triple staticMap. For example:

[...]

Hm, I don't understand what your code does. So Vec3Var!S yields the tuple (Matrix!(byte, 3, 1), Matrix!(short, 3, 3), Matrix!(int, 4, 2)), but what do I do with the tuple?

April 04, 2023

On Monday, 3 April 2023 at 20:17:00 UTC, Salih Dincer wrote:

>

[..]

Oh, I think I get it. Vec3Var in my original post is just a poor choice of name. It's is meant for correct type deduction with U..., not that I want a tuple of types.

April 05, 2023

On Friday, 31 March 2023 at 05:19:48 UTC, Elfstone wrote:

>

[..]

Hi all! I've pushed my code:
https://github.com/Lucipetus/dmd/tree/fix_alias_template_deduction
And here's my test code:
https://github.com/Lucipetus/dmd_test - Run dmd atifti.d

I'll have a look on static assert(isInstance!(Regex, Regex!char)), it still fails, but static assert(is(Vector3!float == Vector3!U, U)) now compiles!

The fix is really, really simple, as I've explained in my last reply: recursively call deduceType, match the output with largely the same code as the original matching routine L2: - as you can see, my matching is simply an abridged version of the original. I think ideally we can share the same code, which requires modifying the original - I don't think I'm qualified to do that. But I think it's a proof of idea. We can do IFTI for alias template!

Another thing. I was thinking of alias AliasT(U:U*) = TempT(U), but found out IFTI couldn't resolve this: https://github.com/Lucipetus/dmd_test/blob/master/iftilim.d void foo(U : U*)(TempT!U v), so why bother?

« First   ‹ Prev
1 2 3