Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive

Comments

Make _d_arrayctor and _d_arraysetctor aware of copy constructor#3177

Closed
edi33416 wants to merge 3 commits intodlang:masterfrom
edi33416:core_array_ctor
Closed

Make _d_arrayctor and _d_arraysetctor aware of copy constructor#3177
edi33416 wants to merge 3 commits intodlang:masterfrom
edi33416:core_array_ctor

Conversation

@edi33416
Copy link
Contributor

@edi33416 edi33416 commented Aug 1, 2020

This PR makes the copy construction of arrays aware of the copy constructor.
IMHO, in the long run, this should be used in #3139 and other similar fixes.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @edi33416! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + druntime#3177"

@edi33416 edi33416 requested review from RazvanN7 and andralex August 1, 2020 17:30
{
auto elem = cast(Unqual!T*)&to[i];
// Copy construction is defined as bit copy followed by postblit.
memcpy(elem, &from[i], element_size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR, but memcpying each element separately is not optimal, and doesn't exploit the postblit advantage over copy ctors. The loop can additionally be completely elided if the type has no postblit.

Copy link
Contributor

@kinke kinke Aug 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, is this stuff actually used at all? The compiler still lowers to the non-templated extern(C) hook...

Edit: Seems totally unused in 2.093, incl. no usages in druntime/Phobos.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. This doesn't get lowered to by the compiler.
As you've pointed out, the compiler lowers to the array function from rt.arrayassign.

The course of action going forward is to hook the call to _d_arrayctor in dmd, as @RazvanN7 pointed out.

Before doing so, we'll have to convert _d_arrayassign, _d_arrayassign_l and _d_arrayassign_r to into templates and then change dmd to call the templated versions of _d_arrayassign and the current templated version of _d_arrayctor and their set cousins _d_arraysetctor and _d_arraysetassign

Copy link
Contributor Author

@edi33416 edi33416 Aug 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this PR, but memcpying each element separately is not optimal, and doesn't exploit the postblit advantage over copy ctors.

I believe the memcpying is done separately so it will be able to correctly destroy previously constructed elements in case a of a throw from the postblit of the current ith element

The loop can additionally be completely elided if the type has no postblit.

I think the memcopying should still be performed.

Copy link
Contributor

@kinke kinke Aug 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When performing a single memcpy, the 'unconstructed' elements >= i will have to be reset (edit: blitted) to T.init in the exception case, yes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you're suggesting that the code should be changed to

try
    {
        static if (__traits(hasPostblit, T))
        {
            memcpy(to, from, element_size * to.length);
            for (i = 0; i < to.length; i++)
            {
                auto elem = cast(Unqual!T*)&to[i];
                postblitRecurse(*elem);
            }
        }
        else static if (__traits(hasCopyConstructor, T))
        {
            for (i = 0; i < to.length; i++)
            {
                to[i].__ctor(from[i]);
            }
        }
        else
        {
            memcpy(to, from, element_size * to.length);
        }
    catch (Exception o)
    {
        /* Destroy, in reverse order, what we've constructed so far
        */
        static if (__traits(hasPostblit, T) || !__traits(hasCopyConstructor, T))
        {
            for (int j = i; j < to.length; j++)
            {
                auto elem = cast(Unqual!T*)&to[i];
                memcpy(elem, &T.init, element_size);
            }
        }
        while (i--)
        {
            auto elem = cast(Unqual!T*)&to[i];
            destroy(*elem);
        }
        throw o;
    }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something similar to this, yes. Elements without postblit/cpctor don't need a try/catch, that's a plain memcpy. T.init is an rvalue and cannot be used for memcpy; there's some emplace version blitting the default initializer which can be used - in case the T.init reset is really necessary (the array wasn't successfully constructed, so is it going to be destructed at some point later? have the elements already been initialized with T.init in the first place?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep this as a separate PR in an attempt to keep the changes small

@edi33416 edi33416 force-pushed the core_array_ctor branch 2 times, most recently from 0a67405 to 6ce8fc0 Compare August 20, 2020 16:16
@RazvanN7
Copy link
Contributor

RazvanN7 commented Nov 9, 2020

@edi33416 Fix the conflicts and let's merge this!

@edi33416
Copy link
Contributor Author

Looks like this has been implemented in #3239

@edi33416 edi33416 closed this Jan 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants