Fix Bugzilla issue 24827: maxElement does not handle opAssign correctly. (#9067)

Rebindable2 did not handle types with opAssign correctly, which affected
both minElement and maxElement. Namely, Rebindable2 assigned to memory
which was not properly initialized when the correct solution in such a
situation is to use copyEmplace. Assignment works when assignment is
just a memcpy, but in the general case, opAssign needs to have a
properly initialized object in order to work correctly. copyEmplace
instead copies the object and then places the copy into the unitialized
memory, so it avoids assigning to uninitialized memory.

This commit also adds additional tests for types with destructors (which
do get opAssign automatically) and types with postblit constructors or
copy constructors to try to ensure that the code is doing the correct
thing in those cases with regards to copying, assignment, and
destruction.

https://issues.dlang.org/show_bug.cgi?id=24829 was found in the process,
and this does not fix that. Namely, types which cannot be assigned to
and which also have a postblit constructor or copy constructor do not
get copied correctly. So, among the tests added here are commented out
tests for that case, since they're an altered version of some of the
enabled tests. However, fixing that issue would be involved enough that
I'm not attempting to fix it at this time.
This commit is contained in:
Jonathan M Davis 2024-10-27 02:16:22 -06:00 committed by GitHub
parent 10076badd8
commit f0c3e4a66b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 419 additions and 8 deletions

View file

@ -3735,6 +3735,47 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(arr.minElement!"a.val".val == 0);
}
// https://issues.dlang.org/show_bug.cgi?id=24827
@safe unittest
{
static struct S
{
int i;
bool destroyed;
this(int i) @safe
{
this.i = i;
}
~this() @safe
{
destroyed = true;
}
bool opEquals()(auto ref S rhs)
{
return this.i == rhs.i;
}
int opCmp()(auto ref S rhs)
{
if (this.i < rhs.i)
return -1;
return this.i == rhs.i ? 0 : 1;
}
@safe invariant
{
assert(!destroyed);
}
}
auto arr = [S(19), S(2), S(145), S(7)];
assert(minElement(arr) == S(2));
}
/**
Iterates the passed range and returns the maximal element.
A custom mapping function can be passed to `map`.
@ -3888,6 +3929,47 @@ if (isInputRange!Range && !isInfinite!Range &&
assert(arr[0].getI == 2);
}
// https://issues.dlang.org/show_bug.cgi?id=24827
@safe unittest
{
static struct S
{
int i;
bool destroyed;
this(int i) @safe
{
this.i = i;
}
~this() @safe
{
destroyed = true;
}
bool opEquals()(auto ref S rhs)
{
return this.i == rhs.i;
}
int opCmp()(auto ref S rhs)
{
if (this.i < rhs.i)
return -1;
return this.i == rhs.i ? 0 : 1;
}
@safe invariant
{
assert(!destroyed);
}
}
auto arr = [S(19), S(2), S(145), S(7)];
assert(maxElement(arr) == S(145));
}
// minPos
/**
Computes a subrange of `range` starting at the first occurrence of `range`'s