D Language Foundation July 2022 Quarterly Meeting Summary

ryuukk_ ryuukk.dev at gmail.com
Mon Aug 29 13:23:03 UTC 2022


> Dennis brought up a discussion about reducing the size of 
> object.d that came up in a PR thread. Specifically, moving 
> things into separate modules, then making object.d a list of 
> public imports would make it easier to maintain a custom 
> object.d.

It is unfortunate that my suggestion was used to push an other 
idea

A public import, is the same problem

My issue with `object.d` is it is shadowing symbols


`destroy` for example

If i want a `destroy` function, i call destroy, but if i made a 
typo in my function or forgot to actually create it, then it'll 
silently call `destroy` from `object.d` without letting you know 
about it, that's what prompted me to make the PR

I now banned the `destroy` name, and instead use `dispose` as 
people on the IRC suggested me to do, wich in restrospec is a 
better word indeed


But i figured i should not expect such changes, so instead i now 
compile with `-betterC` and a custom `object.d` for all of my 
projects

The other unfortunate thing is i now have to deal with issues 
like this, and incorporate hacks into my codebases for LDC: 
https://github.com/ldc-developers/ldc/issues/2425#issuecomment-1193330845


> The consensus was that with D's built-in ability to 
> differentiate symbols (FQN, static imports), this isn't a 
> problem. Iain closed the PR after the meeting, noting that we 
> agreed something needs to be done about object.d, "but not 
> this".


The worst part is that file should not be needed at all to begin 
with


You can't do simple ptr/slice casting without it, and you can't 
use enums without it

If `destroy` is essential to D, then it should be upgraded to a 
builtin and reserved keyword, if it is just an utility function, 
then it shouldn't be in the global scope, or it should be 
prefixed `__destroy`



There needs a way to differentiate functions used for language 
support like `switch` and `cast` with other utility functions, 2 
different concerns


This is what i have to carry to opt out this specific thing 
(luckily i do not use any other features): (see at the end of the 
post)

And one recent issue i had, because of the custom `object.d` 
route:

https://i.imgur.com/Ye9ewJP.png

I couldn't figure out what caused the issue, thanks to Adam, 
compiling with `dmd -v` pointed out the problematic import: 
`import core.sys.windows.threadaux;`

Pay as you go is the way to go, but the experience should be 
drastically improved imo


```D
module object;

alias size_t = typeof(int.sizeof);
alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0);

alias sizediff_t = ptrdiff_t; // For backwards compatibility only.
/**
  * Bottom type.
  * See $(DDSUBLINK spec/type, noreturn).
  */
alias noreturn = typeof(*null);

alias hash_t = size_t; // For backwards compatibility only.
alias equals_t = bool; // For backwards compatibility only.

alias string  = immutable(char)[];
alias wstring = immutable(wchar)[];
alias dstring = immutable(dchar)[];


bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
@nogc nothrow pure @trusted
if (__traits(isScalar, T1) && __traits(isScalar, T2))
{
     if (lhs.length != rhs.length)
         return false;

     static if (T1.sizeof == T2.sizeof
         // Signedness needs to match for types that promote to 
int.
         // (Actually it would be okay to memcmp bool[] and byte[] 
but that is
         // probably too uncommon to be worth checking for.)
         && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == 
__traits(isUnsigned, T2))
         && !__traits(isFloating, T1) && !__traits(isFloating, T2))
     {
         if (!__ctfe)
         {
             // This would improperly allow equality of integers 
and pointers
             // but the CTFE branch will stop this function from 
compiling then.

             import core.stdc.string : memcmp;
             return lhs.length == 0 ||
                 0 == memcmp(cast(const void*) lhs.ptr, cast(const 
void*) rhs.ptr, lhs.length * T1.sizeof);
         }
     }


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

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

             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;
         }
     }
     else
     {

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

pragma(inline, true)
ref at(T)(T[] r, size_t i) @trusted
     // exclude opaque structs due to 
https://issues.dlang.org/show_bug.cgi?id=20959
     if (!(is(T == struct) && !is(typeof(T.sizeof))))
{
     static if (is(immutable T == immutable void))
         return (cast(ubyte*) r.ptr)[i];
     else
         return r.ptr[i];
}

template BaseType(T)
{
     static if (__traits(isStaticArray, T))
         alias BaseType = BaseType!(typeof(T.init[0]));
     else static if (is(immutable T == immutable void))
         alias BaseType = ubyte;
     else static if (is(T == E*, E))
         alias BaseType = size_t;
     else
         alias BaseType = T;
}

template useMemcmp(T1, T2)
{
     static if (T1.sizeof != T2.sizeof)
         enum useMemcmp = false;
     else
     {
         alias B1 = BaseType!T1;
         alias B2 = BaseType!T2;
         enum useMemcmp = __traits(isIntegral, B1) && 
__traits(isIntegral, B2)
            && !( (B1.sizeof < 4 || B2.sizeof < 4) && 
__traits(isUnsigned, B1) != __traits(isUnsigned, B2) );
     }
}



TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from)
{
    const fromSize = from.length * TFrom.sizeof;
    const toLength = fromSize / TTo.sizeof;

    if ((fromSize % TTo.sizeof) != 0)
    {
         //onArrayCastError(TFrom.stringof, fromSize, 
TTo.stringof, toLength * TTo.sizeof);
         assert(0);
    }

    struct Array
    {
        size_t length;
        void* ptr;
    }
    auto a = cast(Array*)&from;
    a.length = toLength; // jam new length
    return *cast(TTo[]*)a;
}



// switch
extern(C) void __switch_error()(string file = __FILE__, size_t 
line = __LINE__)
{
     //__switch_errorT(file, line);
     wasm.abort();
}

extern(C) int __switch(T, caseLabels...)(/*in*/ const scope T[] 
condition) pure nothrow @safe @nogc
{
     // This closes recursion for other cases.
     static if (caseLabels.length == 0)
     {
         return int.min;
     }
     else static if (caseLabels.length == 1)
     {
         return __cmp(condition, caseLabels[0]) == 0 ? 0 : int.min;
     }
     // To be adjusted after measurements
     // Compile-time inlined binary search.
     else static if (caseLabels.length < 7)
     {
         int r = void;
         enum mid = cast(int)caseLabels.length / 2;
         if (condition.length == caseLabels[mid].length)
         {
             r = __cmp(condition, caseLabels[mid]);
             if (r == 0) return mid;
         }
         else
         {
             // Equivalent to (but faster than) condition.length > 
caseLabels[$ / 2].length ? 1 : -1
             r = ((condition.length > caseLabels[mid].length) << 
1) - 1;
         }

         if (r < 0)
         {
             // Search the left side
             return __switch!(T, caseLabels[0 .. mid])(condition);
         }
         else
         {
             // Search the right side
             return __switch!(T, caseLabels[mid + 1 .. 
$])(condition) + mid + 1;
         }
     }
     else
     {
         // Need immutable array to be accessible in pure code, 
but case labels are
         // currently coerced to the switch condition type (e.g. 
const(char)[]).
         pure @trusted nothrow @nogc asImmutable(scope 
const(T[])[] items)
         {
             assert(__ctfe); // only @safe for CTFE
             immutable T[][caseLabels.length] result = 
cast(immutable)(items[]);
             return result;
         }
         static immutable T[][caseLabels.length] cases = 
asImmutable([caseLabels]);

         // Run-time binary search in a static array of labels.
         return __switchSearch!T(cases[], condition);
     }
}

extern(C) int __cmp(T)(scope const T[] lhs, scope const T[] rhs) 
@trusted
     if (__traits(isScalar, T))
{
     // Compute U as the implementation type for T
     static if (is(T == ubyte) || is(T == void) || is(T == bool))
         alias U = char;
     else static if (is(T == wchar))
         alias U = ushort;
     else static if (is(T == dchar))
         alias U = uint;
     else static if (is(T == ifloat))
         alias U = float;
     else static if (is(T == idouble))
         alias U = double;
     else static if (is(T == ireal))
         alias U = real;
     else
         alias U = T;

     static if (is(U == char))
     {
         import core.internal.string : dstrcmp;
         return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
     }
     else static if (!is(U == T))
     {
         // Reuse another implementation
         return __cmp(cast(U[]) lhs, cast(U[]) rhs);
     }
     else
     {
         version (BigEndian)
         static if (__traits(isUnsigned, T) ? !is(T == __vector) : 
is(T : P*, P))
         {
             if (!__ctfe)
             {
                 int c = mem.memcmp(lhs.ptr, rhs.ptr, (lhs.length 
<= rhs.length ? lhs.length : rhs.length) * T.sizeof);
                 if (c)
                     return c;
                 static if (size_t.sizeof <= uint.sizeof && 
T.sizeof >= 2)
                     return cast(int) lhs.length - cast(int) 
rhs.length;
                 else
                     return int(lhs.length > rhs.length) - 
int(lhs.length < rhs.length);
             }
         }

         immutable len = lhs.length <= rhs.length ? lhs.length : 
rhs.length;
         foreach (const u; 0 .. len)
         {
             static if (__traits(isFloating, T))
             {
                 immutable a = lhs.ptr[u], b = rhs.ptr[u];
                 static if (is(T == cfloat) || is(T == cdouble)
                     || is(T == creal))
                 {
                     // Use rt.cmath2._Ccmp instead ?
                     auto r = (a.re > b.re) - (a.re < b.re);
                     if (!r) r = (a.im > b.im) - (a.im < b.im);
                 }
                 else
                 {
                     const r = (a > b) - (a < b);
                 }
                 if (r) return r;
             }
             else if (lhs.ptr[u] != rhs.ptr[u])
                 return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1;
         }
         return (lhs.length > rhs.length) - (lhs.length < 
rhs.length);
     }
}

// This function is called by the compiler when dealing with array
// comparisons in the semantic analysis phase of CmpExp. The 
ordering
// comparison is lowered to a call to this template.
extern(C) int __cmp(T1, T2)(T1[] s1, T2[] s2)
if (!__traits(isScalar, T1) && !__traits(isScalar, T2))
{
     alias U1 = Unqual!T1;
     alias U2 = Unqual!T2;

     static if (is(U1 == void) && is(U2 == void))
         static @trusted ref inout(ubyte) at(inout(void)[] r, 
size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; }
     else
         static @trusted ref R at(R)(R[] r, size_t i) { return 
r.ptr[i]; }

     // All unsigned byte-wide types = > dstrcmp
     immutable len = s1.length <= s2.length ? s1.length : 
s2.length;

     foreach (const u; 0 .. len)
     {
         static if (__traits(compiles, __cmp(at(s1, u), at(s2, 
u))))
         {
             auto c = __cmp(at(s1, u), at(s2, u));
             if (c != 0)
                 return c;
         }
         else static if (__traits(compiles, at(s1, u).opCmp(at(s2, 
u))))
         {
             auto c = at(s1, u).opCmp(at(s2, u));
             if (c != 0)
                 return c;
         }
         else static if (__traits(compiles, at(s1, u) < at(s2, u)))
         {
             if (at(s1, u) != at(s2, u))
                 return at(s1, u) < at(s2, u) ? -1 : 1;
         }
         else
         {
             // TODO: fix this legacy bad behavior, see
             // https://issues.dlang.org/show_bug.cgi?id=17244
             static assert(is(U1 == U2), "Internal error.");
             auto c = (() @trusted => mem.memcmp(&at(s1, u), 
&at(s2, u), U1.sizeof))();
             if (c != 0)
                 return c;
         }
     }
     return (s1.length > s2.length) - (s1.length < s2.length);
}


template Unqual(T : const U, U)
{
     static if (is(U == shared V, V))
         alias Unqual = V;
     else
         alias Unqual = U;
}


// binary search in sorted string cases, also see `__switch`.
private int __switchSearch(T)(/*in*/ const scope T[][] cases, 
/*in*/ const scope T[] condition) pure nothrow @safe @nogc
{
     size_t low = 0;
     size_t high = cases.length;

     do
     {
         auto mid = (low + high) / 2;
         int r = void;
         if (condition.length == cases[mid].length)
         {
             r = __cmp(condition, cases[mid]);
             if (r == 0) return cast(int) mid;
         }
         else
         {
             // Generates better code than "expr ? 1 : -1" on dmd 
and gdc, same with ldc
             r = ((condition.length > cases[mid].length) << 1) - 1;
         }

         if (r > 0) low = mid + 1;
         else high = mid;
     }
     while (low < high);

     // Not found
     return -1;
}
```


More information about the Digitalmars-d-announce mailing list