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