This is an archive of the discontinued LLVM Phabricator instance.

[libcxx] Sprinkle constexpr over compressed_pair
AcceptedPublic

Authored by loladiro on Sep 8 2016, 8:30 PM.

Details

Summary

Without this, unique_ptr is not constant initialized. I've added a test to that extent.
Unfortunately, I believe there's additional work required for C++11, since unique_ptr
uses std::forward internally which is not constexpr until C++14. Doing something about
that is not part of this patch. unique_ptr not being constant initialized is particularly problematic
because it is in libstdc++, so there's applications that rely on this behavior.

Diff Detail

Repository
rL LLVM

Event Timeline

loladiro updated this revision to Diff 70777.Sep 8 2016, 8:30 PM
loladiro retitled this revision from to [libcxx] Sprinkle constexpr over compressed_pair.
loladiro updated this object.
loladiro added reviewers: EricWF, mclow.lists.
loladiro set the repository for this revision to rL LLVM.
loladiro added a subscriber: cfe-commits.
mclow.lists edited edge metadata.Sep 9 2016, 10:45 AM

This all looks good to me - but with a couple nits.

include/memory
2137

Have you tested this on C++11?
I suspect that some of these need to be _LIBCPP_CONSTEXPR_AFTER_CXX11

test/std/utilities/memory/unique.ptr/unique.ptr.runtime/unique.ptr.runtime.ctor/constinit.pass.cpp
16

Shouldn't this test be in the libcxx hierarchy, since it uses internal libcxx features (i.e, _LIBCPP_SAFE_STATIC)?

rsmith added a subscriber: rsmith.Sep 9 2016, 11:48 AM
rsmith added inline comments.
test/std/utilities/memory/unique.ptr/unique.ptr.runtime/unique.ptr.runtime.ctor/constinit.pass.cpp
19–23

This is a bit painful to test without _LIBCPP_SAFE_STATIC, because the contents of a will be all 0 bits on program startup regardless of whether static or dynamic initialization is performed. Something like this might work though:

extern std::unique_ptr<int> a;
void *trample = std::memset(&a, 0xab, sizeof(a));
_LIBCPP_SAFE_STATIC std::unique_ptr<int> a;

int main() {
  // Check that the initialization of 'a' was performed before the initialization of 'trample'.
  for (size_t n = 0; n != sizeof(a); ++n)
    assert(reinterpret_cast<char*>(trample)[n] == 0xab);
  // Put a unique_ptr object back so that the global dtor is valid.
  new (&a) std::unique_ptr<int>;
}
loladiro added inline comments.Sep 9 2016, 12:02 PM
include/memory
2137

Well, as I mentioned there are other problems on C++11, namely forward not being constexpr (so even the simple constructors below can't be constant initialized in C++11). I would REALLY like for unique_ptr to be constant initialized even in C++11 mode. I feel like arguably the standard requires it by marking the constructor constexpr.

test/std/utilities/memory/unique.ptr/unique.ptr.runtime/unique.ptr.runtime.ctor/constinit.pass.cpp
16

I saw this feature was already used in the std/ hierarchy in rL280777, so I thought it was ok, but happy to do whatever you think is best.

19–23

Sounds good.

Small bump here. Is it possible to rewrite these without std::forward, so we can have the const-initialization behavior on C++11 (or is there are different solution?).

mclow.lists added inline comments.Sep 13 2016, 3:03 PM
include/memory
2137

One way to deal with that is to define a call __forward() that is the same as forward() except that it's constexpr in C++11, and have compressed_pair call that.

loladiro added inline comments.Sep 13 2016, 3:07 PM
include/memory
2137

I was worried that something in the definition of forward required C++14 constexpr semantics? Is that not the case. If so this sounds like a good solution, and I can update the patch accordingly.

loladiro updated this revision to Diff 71528.Sep 15 2016, 11:23 AM
loladiro edited edge metadata.

Add private copy of forward (__forward) that is constexpr even in C++11 mode, use test suggested by @rsmith

EricWF edited edge metadata.Sep 15 2016, 2:46 PM

Thanks for working on this. That static initialization bug is ugly.

My only concern with this patch is that adding constexpr to unconstrained constructors is going to result compile errors caused by Clang's eager instantiation.

Also, unlike @mclow.lists, I think we should make std::forward and std::move constexpr in C++11 as an extension. libstdc++ does it so it should be safe. I'm working on a patch that adds this extension now.

More comments to come shortly.

include/memory
2056

I would put this right next to std::forward.

2511

The body of this function is only constexpr in C++14 because it contains two statements.

return base::operator=(static_cast<__compressed_pair&&>(__p)), *this is a constant expression in C++11 though.

2526

This isn't a constant expression until C++14, since both tuple and move are only constexpr in C++14.

(Although I guess two default constructed tuple's would be constexpr in C++11).

test/std/utilities/memory/unique.ptr/unique.ptr.runtime/unique.ptr.runtime.ctor/constinit.pass.cpp
2

This test should live under test/std/utilities/memory/unique.ptr/unique.ptr.single/unique.ptr.single.ctor since unique.ptr.runtime is for unique_ptr's of arrays (e.g. unique_ptr<T[]>). Adding a separate version of this test for unique_ptr<T[]> would also be appreciated.

21

Could this add a test case for a unique_ptr with a non-empty deleter? That will test an entirely different specialization of __compressed_pair.

FYI std::move and std::forward are now constexpr in C++11. There is no need to use __forward.

loladiro updated this revision to Diff 72581.Sep 26 2016, 4:10 PM
loladiro edited edge metadata.

back to _VSTD::forward, address review comments, add tests for non-empty deleter and unique_ptrs of arrays.

mclow.lists accepted this revision.Sep 27 2016, 2:02 PM
mclow.lists edited edge metadata.

This looks good to me.

I appreciate the cleverness in the tests.

This revision is now accepted and ready to land.Sep 27 2016, 2:02 PM

Before I commit this, I just realized

__libcpp_compressed_pair_imp& operator=(const __libcpp_compressed_pair_imp& __p)
    _NOEXCEPT_(is_nothrow_copy_assignable<_T1>::value &&
               is_nothrow_copy_assignable<_T2>::value)
    {
        _T1::operator=(__p.first());
        __second_ = __p.second();
        return *this;
    }

is only constexpr in C++14, right?

loladiro updated this revision to Diff 72712.Sep 27 2016, 2:39 PM
loladiro edited edge metadata.

Some extra _AFTER_CXX11 that may be necessary - please double check me here, C++11 constexpr rules are not exactly my specialty ;).

Bump, please do take another close look at the latest update, particularly with respecting to marking things constexpr that are not so in C++11.

Looks better than before.

Ok then, will commit.

Is this patch relevant any more?

Herald added a project: Restricted Project. · View Herald TranscriptJul 3 2019, 1:44 PM