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.