Index: llvm/include/llvm/ADT/PropagateConst.h =================================================================== --- /dev/null +++ llvm/include/llvm/ADT/PropagateConst.h @@ -0,0 +1,198 @@ +//==- llvm/ADT/PropagateConst.h - propagate const to pointees ----*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// PropagateConst is a wrapper around pointer types that allows only const +// access to the pointee when the pointer is accessed through a const-access +// path. +// +// llvm::PropagateConst is based on std::experimental::propagate_const from +// Library Fundamentals Technical Specification 2 as implemented in libcxx. + +#include "llvm/Support/Compiler.h" + +#include +#include +#include + +namespace llvm { + +template class PropagateConst; + +template +inline const PointerLike &get_underlying(const PropagateConst &P); + +template +inline PointerLike &get_underlying(PropagateConst &P); + +template class PropagateConst { +public: + typedef typename std::remove_reference())>::type element_type; + + static_assert( + !std::is_array::value, + "Instantiation of PropagateConst with an array type is ill-formed."); + static_assert( + !std::is_reference::value, + "Instantiation of PropagateConst with a reference type is ill-formed."); + static_assert(!(std::is_pointer::value && + std::is_function< + typename std::remove_pointer::type>::value), + "Instantiation of PropagateConst with a function-pointer type " + "is ill-formed."); + static_assert( + !(std::is_pointer::value && + std::is_same::type>::type, + void>::value), + "Instantiation of PropagateConst with a pointer to (possibly " + "cv-qualified) void is ill-formed."); + +private: + template + static element_type *getPointer(OtherPointerLike *U) { + return U; + } + + template + static element_type *getPointer(OtherPointerLike &U) { + return getPointer(U.get()); + } + + template + static const element_type *getPointer(const OtherPointerLike *U) { + return U; + } + + template + static const element_type *getPointer(const OtherPointerLike &U) { + return getPointer(U.get()); + } + + template + struct isPropagateConst : std::false_type {}; + + template + struct isPropagateConst> : std::true_type {}; + + PointerLike Ptr_; + +public: + template + friend const OtherPointerLike & ::llvm::get_underlying( + const PropagateConst &U); + template + friend OtherPointerLike & ::llvm::get_underlying( + PropagateConst &U); + + PropagateConst() = default; + + PropagateConst(const PropagateConst &) = delete; + + PropagateConst(PropagateConst &&) = default; + + template < + class OtherPointerLike, + typename std::enable_if< + !std::is_convertible::value && + std::is_constructible::value, + bool>::type = true> + explicit PropagateConst(PropagateConst &&U) + : Ptr_(std::move(llvm::get_underlying(U))) {} + + template < + class OtherPointerLike, + typename std::enable_if< + std::is_convertible::value && + std::is_constructible::value, + bool>::type = false> + PropagateConst(PropagateConst &&U) + : Ptr_(std::move(llvm::get_underlying(U))) {} + + template < + class OtherPointerLike, + typename std::enable_if< + !std::is_convertible::value && + std::is_constructible::value && + !isPropagateConst< + typename std::decay::type>::value, + bool>::type = true> + explicit PropagateConst(OtherPointerLike &&__u) + : Ptr_(std::forward(__u)) {} + + template < + class OtherPointerLike, + typename std::enable_if< + std::is_convertible::value && + std::is_constructible::value && + !isPropagateConst< + typename std::decay::type>::value, + bool>::type = false> + PropagateConst(OtherPointerLike &&__u) + : Ptr_(std::forward(__u)) {} + + PropagateConst &operator=(const PropagateConst &) = delete; + + PropagateConst &operator=(PropagateConst &&) = default; + + template + PropagateConst &operator=(PropagateConst &&U) { + Ptr_ = std::move(llvm::get_underlying(U)); + return *this; + } + + template ::type>::value>> + PropagateConst &operator=(OtherPointerLike &&__u) { + Ptr_ = std::forward(__u); + return *this; + } + + const element_type *get() const { return getPointer(Ptr_); } + + element_type *get() { return getPointer(Ptr_); } + + explicit operator bool() const { return get() != nullptr; } + + const element_type *operator->() const { return get(); } + + template ::value>> + operator const element_type *() const { + return get(); + } + + const element_type &operator*() const { return *get(); } + + element_type *operator->() { return get(); } + + template ::value>> + operator element_type *() { + return get(); + } + + element_type &operator*() { return *get(); } + + void swap(PropagateConst &P) { + using std::swap; + swap(Ptr_, P.Ptr_); + } + + friend void swap(PropagateConst& lhs, PropagateConst& rhs) + { + using std::swap; + swap(lhs.Ptr_, rhs.Ptr_); + } +}; + +} // namespace llvm Index: llvm/unittests/ADT/CMakeLists.txt =================================================================== --- llvm/unittests/ADT/CMakeLists.txt +++ llvm/unittests/ADT/CMakeLists.txt @@ -43,6 +43,7 @@ PointerUnionTest.cpp PostOrderIteratorTest.cpp PriorityWorklistTest.cpp + PropagateConstTest.cpp RangeAdapterTest.cpp SCCIteratorTest.cpp STLExtrasTest.cpp Index: llvm/unittests/ADT/PropagateConstTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/ADT/PropagateConstTest.cpp @@ -0,0 +1,116 @@ +//===-*- C++ -*-===// +//=- llvm/unittest/ADT/PropagateConstTest.cpp - PropagateConst unit tests -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/PropagateConst.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +enum class ConstQualifier { Const, NonConst }; + +struct A { + ConstQualifier foo() const { return ConstQualifier::Const; } + + ConstQualifier foo() { return ConstQualifier::NonConst; } +}; + +struct B : A {}; + +TEST(PropagateConst, NonConstPropagation) { + A a; + llvm::PropagateConst pc(&a); + + EXPECT_EQ(pc->foo(), ConstQualifier::NonConst); + EXPECT_EQ(pc.get()->foo(), ConstQualifier::NonConst); + EXPECT_EQ((*pc).foo(), ConstQualifier::NonConst); +} + +TEST(PropagateConst, ConstPropagation) { + A a; + const llvm::PropagateConst pc(&a); + + EXPECT_EQ(pc->foo(), ConstQualifier::Const); + EXPECT_EQ(pc.get()->foo(), ConstQualifier::Const); + EXPECT_EQ((*pc).foo(), ConstQualifier::Const); +} + +TEST(PropagateConst, DefaultConstruction) { + // Use std::unique_ptr as the default constructor of PropagateConst does + // not zero-initialise the member pointer. + llvm::PropagateConst> pc; + + EXPECT_FALSE(pc); +} + +TEST(PropagateConst, OperatorBool) { + A a; + llvm::PropagateConst pc(&a); + + EXPECT_TRUE(pc); +} + +TEST(PropagateConst, MoveConstruction) { + PropagateConst> pc1(llvm::make_unique()); + PropagateConst> pc2(std::move(pc1)); + + EXPECT_FALSE(pc1); + EXPECT_TRUE(pc2); +} + +TEST(PropagateConst, MoveAssignment) { + PropagateConst> pc1(llvm::make_unique()); + PropagateConst> pc2; + pc2 = std::move(pc1); + + EXPECT_FALSE(pc1); + EXPECT_TRUE(pc2); +} + +TEST(PropagateConst, ConvertingConstructor) { + B b; + + PropagateConst pc(&b); + + EXPECT_TRUE(pc); +} + +TEST(PropagateConst, ExplicitConvertingConstructor) { + PropagateConst> pc(new A()); + + EXPECT_TRUE(pc); +} + +TEST(PropagateConst, MemberSwap) { + PropagateConst> pc1; + PropagateConst> pc2(new A()); + + A* p = pc2.get(); + + pc1.swap(pc2); + + EXPECT_EQ(pc1.get(), p); +} + +TEST(PropagateConst, Swap) { + PropagateConst> pc1; + PropagateConst> pc2(new A()); + + A* p = pc2.get(); + + using std::swap; + swap(pc1, pc2); + + EXPECT_EQ(pc1.get(), p); +} + +} // end anonymous namespace +