Template type inference problem

Dennis dkorpel at gmail.com
Fri Oct 4 10:12:04 UTC 2024


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


More information about the Digitalmars-d mailing list