Jump to page: 1 2
Thread overview
Template type inference problem
Oct 02
Manu
Oct 02
kdevel
Oct 02
Manu
Oct 02
Manu
Oct 02
Manu
Oct 04
Dennis
Oct 09
Manu
October 02
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...


October 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) [];


October 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) {}

...

Looking at the protofunction, the test() function should look like this:

void test()
{
    int[] x;
    int* y;
    f(x, y);
}

SDB@79

October 03
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.


October 03
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.


October 03
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.
>


October 03
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.
October 03

On Wednesday, 2 October 2024 at 23:43:43 UTC, Manu wrote:

>

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);

A third possibility, the following code, also works:

const int*[] a;  
const int* b;

f(a, &b);

Interesting!

SDB@79

October 03

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);
}

I think it's a bug.

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

October 04

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 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?

--- 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.

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:

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.)

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.

« First   ‹ Prev
1 2