www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Template type inference problem

reply Manu <turkeyman gmail.com> writes:
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
next sibling parent reply kdevel <kdevel vogtner.de> writes:
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
next sibling parent Manu <turkeyman gmail.com> writes:
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:
 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.
Yes, the `y` argument is `const(int*)*`, so `T` must be inferred `int*`
 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
prev sibling parent reply Manu <turkeyman gmail.com> writes:
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:
 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.
Yes, the `y` argument is `const(int*)*`, so `T` must be inferred `int*`
 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
parent Salih Dincer <salihdb hotmail.com> writes:
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
prev sibling next sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
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
parent Manu <turkeyman gmail.com> writes:
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:
 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
That's not the code in question.
Oct 02
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
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
parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
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
parent reply Manu <turkeyman gmail.com> writes:
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:
 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
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! :P
Oct 09
parent Salih Dincer <salihdb hotmail.com> writes:
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! 
 :P
If 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