Thread overview
Cannot always deduce template arguments when using implicitly cast array literals
Jul 23
Rekel
Jul 23
Rekel
July 23

After simplifying a part of my code I found the following code cannot deduce the template arguments, but I have no clue why.

void foo(L : T[L1][L2], uint L1, uint L2, T, R:
		T[L1])(const L left, const R right) {
	// Function
}

void bar(uint L)(float[L] l) {
	// Function
}

void main(string[] args) {
	foo2([1.0f, 2, 3, 4, 5, 6, 7, 8]); // implicitly cast to float[8], works
	float[4][1] l = [[1, 2, 3, 4]];
	foo!(typeof(l), 4, 1, float, float[4])(l, [1.0f, 2, 3, 4]); // also works
	foo(l, [1.0f, 2, 3, 4]); // cannot deduce function from argument types (?)
}

As one can see, implicitly casting array literals in templates works fine in the case of bar, as does explicit use of templates in the case of foo, but for some reason foo does not manage to deduce its arguments like bar does.
The type of l is well known, and given the template, the type of R should thus be known as well. T[L1] should be deduced to float[4].
Did I make a mistake in the argument list? I know things like the order are sometimes issues, but that seems fine to me here. Seeing as bar functions fine (as does foo when R and right are removed) I'm at a loss.

Thanks you in advance!

July 23

On Friday, 23 July 2021 at 13:53:27 UTC, Rekel wrote:

>

After simplifying a part of my code I found the following code cannot deduce the template arguments, but I have no clue why.

void foo(L : T[L1][L2], uint L1, uint L2, T, R:
		T[L1])(const L left, const R right) {
	// Function
}

void bar(uint L)(float[L] l) {
	// Function
}

void main(string[] args) {
	bar([1.0f, 2, 3, 4, 5, 6, 7, 8]); // implicitly cast to float[8], works
	float[4][1] l = [[1, 2, 3, 4]];
	foo!(typeof(l), 4, 1, float, float[4])(l, [1.0f, 2, 3, 4]); // also works
	foo(l, [1.0f, 2, 3, 4]); // cannot deduce function from argument types (?)
}

As one can see, implicitly casting array literals in templates works fine in the case of bar, as does explicit use of templates in the case of foo, but for some reason foo does not manage to deduce its arguments like bar does.

Looks like the problem is that the compiler is interpreting [1.0f, 2, 3, 4] as a dynamic array (float[]) instead of a static array (float[4]).

Array literals are treated as dynamic arrays by default, and as static arrays only in specific contexts where the compiler is able to determine that a static array is required. Unfortunately, the algorithm it uses for this is not very sophisticated, and does not take type specializations (like R : T[L1]) into account.

There are two ways you can solve this. The first is to change the type of right from const R to const T[L1], which removes the type specialization:

void foo(L : T[L1][L2], uint L1, uint L2, T)
        (const L left, const T[L1] right)
{
	// Function
}

The second is to use std.array.staticArray to manually cast the array literal to a static array:

    import std.array: staticArray;
    foo(l, staticArray!([1.0f, 2, 3, 4]));
July 23

On Friday, 23 July 2021 at 14:41:41 UTC, Paul Backus wrote:

>

The first is to change the type of right from const R to const T[L1], which removes the type specialization:

Thanks for suggesting that fix, removing R altogether is a very simple solution I hadn't considered. :)

>

Unfortunately, the algorithm it uses for this is not very sophisticated, and does not take type specializations (like R : T[L1]) into account.

This seems like a slight oversight to me, do you know if there would be any chance of this changing in the future?