digitalmars.D - Template type inference problem
- Manu (18/18) Oct 02 Does anyone understand why this doesn't work?
- kdevel (3/21) Oct 02 The type of x is int*[] and not const(int) [];
- Manu (12/37) Oct 02 Yes, the `y` argument is `const(int*)*`, so `T` must be inferred `int*`
- Manu (17/57) Oct 02 Here's a clearer presentation of the issue:
- Salih Dincer (9/19) Oct 02 A third possibility, the following code, also works:
- Salih Dincer (12/17) Oct 02 Looking at the protofunction, the test() function should look
- Manu (3/21) Oct 02 That's not the code in question.
- Timon Gehr (2/25) Oct 02 Yes, I think it should work.
- Steven Schveighoffer (18/28) Oct 03 I think it's a bug.
- Dennis (81/82) Oct 04 If you separate the two parameters into two functions:
- Timon Gehr (10/14) Oct 04 ```d
- Salih Dincer (35/49) Oct 04 Hi Manu, what did you do, any progress? So why don't you use 2
- Manu (5/52) Oct 09 I did nothing. I just did work-around and moved on... but I think this i...
- Salih Dincer (39/44) Oct 09 If it's not too special, in what application will you use this?
Does anyone understand why this doesn't work? void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } error : template `f` is not callable using argument types `!()(int*[], const(int*)*)` Candidate is: `f(T)(const(T)[] x, const(T)* y)` Should this work? It looks like it should work to me. ...assuming T is inferred to be `int*`... which it's not clear why it wouldn't be? The argument `y` is an exact match. The argument `x` requires a const promotion, and then T can be inferred correctly. Perhaps it's the order of that operation that it can't deal with? I kinda reckon this is a bug...
Oct 02
On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:Does anyone understand why this doesn't work? void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } error : template `f` is not callable using argument types `!()(int*[], const(int*)*)` Candidate is: `f(T)(const(T)[] x, const(T)* y)` Should this work? It looks like it should work to me. ...assuming T is inferred to be `int*`... which it's not clear why it wouldn't be? The argument `y` is an exact match.The second actual parameter to f!int is &y not y.The argument `x` requires a const promotion,The type of x is int*[] and not const(int) [];
Oct 02
On Wed, 2 Oct 2024 at 20:06, kdevel via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:Yes, the `y` argument is `const(int*)*`, so `T` must be inferred `int*`Does anyone understand why this doesn't work? void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } error : template `f` is not callable using argument types `!()(int*[], const(int*)*)` Candidate is: `f(T)(const(T)[] x, const(T)* y)` Should this work? It looks like it should work to me. ...assuming T is inferred to be `int*`... which it's not clear why it wouldn't be? The argument `y` is an exact match.The second actual parameter to f!int is &y not y.The argument `x` requires a const promotion, The type of x is int*[] and not const(int) [];Right, `x` is `int*[]`, and matching the argument `const(T)[]` required const promotion `int*[]` -> `const(int*)[]`, which is a perfectly normal promotion. In which case, `T` is `int*`, matching with `y`, and so `T` can be properly inferred. If you remove the `*` from `x` and `y`, the `T` inference works properly... so something about `T` being inferred as `int*` rather than an `int` causes the type inference to fail. The non-uniformity looks like a bug, unless there's a reasonable explanation that I've missed.
Oct 02
Here's a clearer presentation of the issue: void f(T)(const(T)[] x, const(T)* y) {} void test() { // this works; f(T) is inferred as f!int int[] x; const int y; f(x, &y); // fails: expected that f(T) is inferred as f!(int*) int*[] a; const int* b; f(a, &b); } Removing `const` from `y` and `b` makes the problem go away, so I think this is a bug in some interaction between the type deduction logic and parameter const promotion? On Thu, 3 Oct 2024 at 09:34, Manu <turkeyman gmail.com> wrote:On Wed, 2 Oct 2024 at 20:06, kdevel via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:Yes, the `y` argument is `const(int*)*`, so `T` must be inferred `int*`Does anyone understand why this doesn't work? void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } error : template `f` is not callable using argument types `!()(int*[], const(int*)*)` Candidate is: `f(T)(const(T)[] x, const(T)* y)` Should this work? It looks like it should work to me. ...assuming T is inferred to be `int*`... which it's not clear why it wouldn't be? The argument `y` is an exact match.The second actual parameter to f!int is &y not y.The argument `x` requires a const promotion, The type of x is int*[] and not const(int) [];Right, `x` is `int*[]`, and matching the argument `const(T)[]` required const promotion `int*[]` -> `const(int*)[]`, which is a perfectly normal promotion. In which case, `T` is `int*`, matching with `y`, and so `T` can be properly inferred. If you remove the `*` from `x` and `y`, the `T` inference works properly... so something about `T` being inferred as `int*` rather than an `int` causes the type inference to fail. The non-uniformity looks like a bug, unless there's a reasonable explanation that I've missed.
Oct 02
On Wednesday, 2 October 2024 at 23:43:43 UTC, Manu wrote:Here's a clearer presentation of the issue: ```d void f(T)(const(T)[] x, const(T)* y) {} void test() { // this works; f(T) is inferred as f!int int[] x; const int y; f(x, &y); ```A third possibility, the following code, also works: ```d const int*[] a; const int* b; f(a, &b); ``` Interesting! SDB 79
Oct 02
On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:Does anyone understand why this doesn't work? ```d void f(T)(const(T)[] x, const(T)* y) {} ``` ...Looking at the protofunction, the test() function should look like this: ```d void test() { int[] x; int* y; f(x, y); } ``` SDB 79
Oct 02
On Wed, 2 Oct 2024 at 20:16, Salih Dincer via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:That's not the code in question.Does anyone understand why this doesn't work? ```d void f(T)(const(T)[] x, const(T)* y) {} ``` ...Looking at the protofunction, the test() function should look like this: ```d void test() { int[] x; int* y; f(x, y); } ``` SDB 79
Oct 02
On 10/2/24 10:55, Manu wrote:Does anyone understand why this doesn't work? void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } error : template `f` is not callable using argument types `!()(int*[], const(int*)*)` Candidate is: `f(T)(const(T)[] x, const(T)* y)` Should this work? It looks like it should work to me. ...assuming T is inferred to be `int*`... which it's not clear why it wouldn't be? The argument `y` is an exact match. The argument `x` requires a const promotion, and then T can be inferred correctly. Perhaps it's the order of that operation that it can't deal with? I kinda reckon this is a bug...Yes, I think it should work.
Oct 02
On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:Does anyone understand why this doesn't work? ```d void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } ```I think it's a bug. ```d void f(T)(const(T)[] x, const(T)* y) {} void f2(T)(const(T)[] x) { pragma(msg, T);} void f3(T)(const(T)* y) { pragma(msg, T);} void test() { int*[] x; const int* y; f2(x); // int * f3(&y); // const(int)* f!(const(int)*)(x, &y); // ok } ``` I tried reversing the order to see if it figures out to use `const(int)*`, but it doesn't work. -Steve
Oct 03
On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:Does anyone understand why this doesn't work?If you separate the two parameters into two functions: ``` void f0(T)(const(T)[] x) { pragma(msg, "f0.T = ", T); } void f1(T)(const(T)* x) { pragma(msg, "f1.T = ", T); } ``` And call `f0(x); f1(&y)`, you get: ``` f0.T = int* f1.T = const(int)* ``` [The current logic](https://github.com/dlang/dmd/blob/f0a2ac5588c780424f1f375b929e842ff07961a2/compiler/src/dmd/dtem late.d#L1525-L1538) can't unify these two deductions of `T`. Why is T inferred as `const(int)*` when it could also match `int*` here? The function responsible for matching different const levels is `deduceTypeHelper`, and when you match const(U) with const(T), it strips away only the top level const. So `const(int*)` becomes `const(int)*`, but not `int*`. So why not strip all levels? ```diff --- a/compiler/src/dmd/dtemplate.d +++ b/compiler/src/dmd/dtemplate.d -1122,6 +1122,10 MATCH deduceTypeHelper(Type t, out Type at, Type tparam) return MATCH.exact; } case X(MODFlags.const_, MODFlags.const_): + { + at = t.unqualify(tparam.mod); + return MATCH.exact; + } case X(MODFlags.wild, MODFlags.wild): ``` Even doing this just for `const` breaks Phobos, and probably many other projects. ```D void formatValueImpl(Writer, T, Char)(auto ref Writer w, scope const(T) obj, /*...*/) /*...*/ { // Error: cannot implicitly convert expression `obj` of type `const(char[])` to `char[]` Unqual!(const(StringTypeOf!T)) val = obj; // for `alias this`, see bug5371 formatRange(w, val, f); } ``` So perhaps the unification logic can be improved. There's already a check for unifying classes based on implicit conversion, but it only works in one order: ```D void f(T)(const T* x, const T* y) { } void main() { const Object o; const Throwable t; f(&o, &t); // Fine f(&t, &o); // Error f!Object(&t, &o); // Fine } ``` This doesn't work with mutable parameters btw, because pointers to class types are only covariant when they are const. (Why is that? I don't know.) ```D static assert(is(Throwable* : Object*)); // Fails static assert(is(const(Throwable)* : const(Object)*)); // Passes ``` Without const AND without pointers there is logic to find a common type, independent of order though: ``` void f(T)(T x, T y) { } void main() { Object o; Throwable t; f(o, t); // Fine f(t, o); // Fine } ``` So yeah... I might be missing good rationale, but it looks like an inconsistent mess right now.
Oct 04
On 10/4/24 12:12, Dennis wrote:This doesn't work with mutable parameters btw, because pointers to class types are only covariant when they are const. (Why is that? I don't know.)```d Throwable a; Object* b = &a; *b = new Object; // modifies `a` assert(typeid(a) is typeid(Object)); throw a; // throwing something that is not a Throwable ``` Technically it would be enough that the pointer itself does not allow writes, but `const` is transitive.
Oct 04
On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:Does anyone understand why this doesn't work? ```d void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } /* Error: template `f` is not callable using argument types `!()(int*[], const(int*)*)` Candidate is: `f(T)(const(T)[] x, const(T)* y)` */ ``` Should this work? It looks like it should work to me.Hi Manu, what did you do, any progress? So why don't you use 2 aliases like T, R? For example: ```d template func(T : const T, R : const R) { void func(const(T)[] x, const(R)* y)/* void func(T[] x, R* y)//*/ { typeid(x).writeln(": ", T.stringof); typeid(y).writeln(": ", R.stringof); } } import std.stdio; void main() { const int[] x; //const int y; func(x, &y); /* Output: const(int)[]: int const(int)*: int */ const int* [] a; //const int* b; func(a, &b); /* Output const(const(int)*)[]: const(int)* const(const(int)*)*: int* */ } ``` SDB79
Oct 04
On Sat, 5 Oct 2024 at 10:26, Salih Dincer via Digitalmars-d < digitalmars-d puremagic.com> wrote:On Wednesday, 2 October 2024 at 08:55:15 UTC, Manu wrote:I did nothing. I just did work-around and moved on... but I think this is definitely a bug. Just another random edge case in a sea of annoying edge cases! :PDoes anyone understand why this doesn't work? ```d void f(T)(const(T)[] x, const(T)* y) {} void test() { int*[] x; const int* y; f(x, &y); } /* Error: template `f` is not callable using argument types `!()(int*[], const(int*)*)` Candidate is: `f(T)(const(T)[] x, const(T)* y)` */ ``` Should this work? It looks like it should work to me.Hi Manu, what did you do, any progress? So why don't you use 2 aliases like T, R? For example: ```d template func(T : const T, R : const R) { void func(const(T)[] x, const(R)* y)/* void func(T[] x, R* y)//*/ { typeid(x).writeln(": ", T.stringof); typeid(y).writeln(": ", R.stringof); } } import std.stdio; void main() { const int[] x; //const int y; func(x, &y); /* Output: const(int)[]: int const(int)*: int */ const int* [] a; //const int* b; func(a, &b); /* Output const(const(int)*)[]: const(int)* const(const(int)*)*: int* */ } ``` SDB79
Oct 09
On Wednesday, 9 October 2024 at 07:02:32 UTC, Manu wrote:I did nothing. I just did work-around and moved on... but I think this is definitely a bug. Just another random edge case in a sea of annoying edge cases! :PIf it's not too special, in what application will you use this? Please consider me as an amateur who is new to D. Because I am a programmer who almost never uses const. I have a hard time understanding what the benefits of what you are doing are. Okay, this code (single template parameter) looks very appealing to me too: ```d template S(T) { auto S(const(T)[] data, const(T) *sum) => Result(data, sum); struct Result { const(T)[] data; const(T) *total; auto sum() const => *total; string id; auto toString() const => format("%s: %s, %s%s", id, sum, T.stringof, data); } } import std; void main() { auto arr = [1, 2, 3, 4, 5]; auto sum = arr.sum; auto s = arr.S(&sum); s.id = "Sum of the Elements"; s.writeln;// Sum of the Elements: 15, int[1, 2, 3, 4, 5] ++sum; assert(s.sum == 16); } ``` But even if sum cannot be changed in the returned Result, since the data array is already copied, doesn't the fact that it is const change anything? SDB 79
Oct 09