Now, member structs won't become const due to transitiveness and the
code in `std.format` can choose (erroneously) non-cost toString()
functions of member structs.
An expert has to decide whether this is worth the breaking change. Now,
code that uses `const` tuples and calls toString directly on them will
break. Alternatively, we could use
[Template this parameters](https://dlang.org/spec/template.html#template_this_parameter).
That way, we get a template parameter with the real type of this. Then we can
cast away constness of `this` when we now for certain that it isn't,
since `is(T != typeof(this))` (where T is the template this parameter).
Of course, this implies making toString `@trusted` too.
This might also lead to unforeseen bugs when `const` is cast away but
the member objects are actually const. I'm not sure how this works.
Fwiw, currently std.format and `std.conv: to` already intercepts const tuples,
thus it at least won't call toString directly. The breaking change will
only effect code that calls toString on const tuples directly.
That's why I have added non-prescritive tests. If something in
std.format changes, they'll be alerted and can then decide whether to
change the tests or whether this module also needs work, in case this
would lead a bigger breaking change.
Followup to #10645.
* Use formattedWrite instead of format
* Remove workaround for shared objects in formattedWrite
The tests are still working. Is it safe to assume that the workaround
would've only been merged if there is a test-case checking it?
* Remove sharedToString workaround
For shared classes, it prints the mangled name, for struct pointers it will
print the adress.
This version of Tuple.toString won't work anymore in @safe code in
general if the tuple contains anything descended from Object. I think
this is correct even if a breaking change, since dealing with this issue
should be the responsibility of the caller.
* Comment to existing code
* Reference the bug that a unit test exemplifies
* Fix#10538 - Cannot swap a std.typecons.Tuple
Co-authored-by: Elias Batek <0xEAB@users.noreply.github.com>
* De-optimize `opAssign` of `std.typecons.Tuple`
---------
Co-authored-by: Elias Batek <0xEAB@users.noreply.github.com>
Co-authored-by: Elias Batek <desisma@heidel.beer>
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.
The second opCast overload is necessary to allow built-in implicit
conversions (e.g., mutable to const) to be performed explicitly via a
cast expression. Nullable.opEquals depends on this behavior.
If there's a constructor that looks like a copy constructor except that
it takes an rvalue instead of taking the argument by ref, then that type
can't have a copy constructor, and one of Tuple's constructors was
causing that problem. So, this fixes it so that it doesn't.
This should make it so that a variable of type Nullable!T has the same
behavior with regards to destruction as a variable of type T even when
it's const, immutable, or shared.
Generally, if a type has a default value, it should be safe to assume
that it's OK to call the destructor on a default-initialized variable.
The converse is not compatible with `move`, and goes against
implementing non-copyable types.
Rebindable2 is a simplified version of std.typecons.Rebindable that clears up every special case: classes, arrays and structs now have the same struct.
Whichever type you instantiate `Rebindable2` with, you always get the same type out by calling `value.get` on the resulting container.
Also use this type to simplify the parts of Phobos we previously used `Rebindable` for.
* typecons: fix use-after-scope bug in RefCounted unittest
The access through `p` to `rc1` outside the scope of `rc1` is undefined behavior. We have to artificially end the scope of `rc1` and `rc2`, such that within the same scope we can check that the destructor calls do the correct thing of nulling the `_store`.
* fixup
* fixup