Without -betterC, the following code compiles:
bool fine() {
float[2] a;
return a == a;
}
It does exactly what you'd expect, and I'm happy. With -betterC however:
bool error() {
float[2] a;
return a == a; // Error: `TypeInfo` cannot be used with -betterC.
}
I don't know why dmd needs to emit type info for array comparisons. The type is clearly known at compile time. I don't see why this wouldn't be possible in -betterC.
By itself, this wouldn't be a massive issue, because we can also do
bool fine() {
float[2] a;
return a[] == a[];
}
And now this will compile and work as expected in -betterC. In fact I even prefer this syntax personally since it's more consistent with other array operations.
But this doesn't work for multi-dimensional arrays.
bool error() {
float[2][2] a;
return a == a; // Error: `TypeInfo` cannot be used with -betterC.
return a[] == a[]; // Error: `TypeInfo` cannot be used with -betterC.
}
And it definitely should work - at least one of these should work.
Comment #1 by blatblatnik — 2021-04-30T20:40:48Z
It's also important to point out that fixing the first issue (type[N] == type[N]) would also fix the second issue.
The only reason type[N][] == type[N][] doesn't work, is because it internally tries to compare every element in the two slices, and every element just happens to be a type[N].
So if type[N] == type[N] is fixed, both types of notation would work.
Comment #2 by blatblatnik — 2021-04-30T20:50:13Z
It also seems like the fixed size array comparisons (type[N] == type[N]) are implemented internally in DMD. However slice comparisons (type[N][] == type[N][]) are implemented in the runtime.
The compiler converts
float[2][2] a;
bool b = a[] == b[];
into:
float[2][2] a;
bool b = core.internal.array.equality.__equals!(float[2], float[2])(a[], a[]);
And this __equals template is something we can change very easily without digging through the compiler.
The offending lines in this function effectively try to do:
foreach(i; 0 .. a.length)
if (a[i] != a[i])
return false;
return true;
But a[i] is a float[2], which can't be compared in -betterC..
If we had a special case for arrays that did this instead
foreach(i; 0 .. a.length)
if (a[i][] != a[i][])
return false;
return true;
Then at least type[N][] == type[N][] would work, until a better fix was found for type[N] == type[N].
Comment #3 by blatblatnik — 2021-04-30T20:58:18Z
Here's a "fixed" version of core.internal.array.equality.__equals that acts correctly in betterC. I'm sure it could be done better but at least it works.
bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs)
if (!__traits(isScalar, T1) || !__traits(isScalar, T2))
{
if (lhs.length != rhs.length)
return false;
if (lhs.length == 0)
return true;
static if (useMemcmp!(T1, T2))
{
if (!__ctfe)
{
static bool trustedMemcmp(scope T1[] lhs, scope T2[] rhs) @trusted @nogc nothrow pure
{
pragma(inline, true);
import core.stdc.string : memcmp;
return memcmp(cast(void*) lhs.ptr, cast(void*) rhs.ptr, lhs.length * T1.sizeof) == 0;
}
return trustedMemcmp(lhs, rhs);
}
}
foreach (const i; 0 .. lhs.length)
{
static if (__traits(isStaticArray, at(lhs, 0))) // "Fix" for -betterC
{
if (at(lhs, i)[] != at(rhs, i)[]) // T1[N] != T2[N] doesn't compile with -betterC.
return false;
}
else
{
if (at(lhs, i) != at(rhs, i))
return false;
}
}
return true;
}
Comment #4 by robert.schadek — 2024-12-13T19:16:03Z