diff --git a/mfbt/NotNull.h b/mfbt/NotNull.h index af2e31237e4f..b4d6915d66f9 100644 --- a/mfbt/NotNull.h +++ b/mfbt/NotNull.h @@ -63,6 +63,7 @@ // for the last one, where the handle type is |void|. See below. #include "mozilla/Assertions.h" +#include "mozilla/Move.h" #include namespace mozilla { @@ -85,8 +86,8 @@ namespace mozilla { // - It does not auto-convert from a base pointer. Implicit conversion from a // less-constrained type (e.g. T*) to a more-constrained type (e.g. // NotNull) is dangerous. Creation and assignment from a base pointer can -// only be done with WrapNotNull(), which makes them impossible to overlook, -// both when writing and reading code. +// only be done with WrapNotNull() or MakeNotNull<>(), which makes them +// impossible to overlook, both when writing and reading code. // // - When initialized (or assigned) it is checked, and if it is null we abort. // This guarantees that it cannot be null. @@ -102,10 +103,12 @@ template class NotNull { template friend NotNull WrapNotNull(U aBasePtr); + template + friend NotNull MakeNotNull(Args&&... aArgs); T mBasePtr; - // This constructor is only used by WrapNotNull(). + // This constructor is only used by WrapNotNull() and MakeNotNull(). template explicit NotNull(U aBasePtr) : mBasePtr(aBasePtr) {} @@ -152,6 +155,22 @@ WrapNotNull(const T aBasePtr) return notNull; } +// Allocate an object with infallible new, and wrap its pointer in NotNull. +// |MakeNotNull>(args...)| will run |new Ob(args...)| +// and return NotNull>. +template +NotNull +MakeNotNull(Args&&... aArgs) +{ + // Extract the pointee type from what T's dereferencing operator returns + // (which could be a reference to a const type). + using Pointee = typename mozilla::RemoveConst< + typename mozilla::RemoveReference())>::Type>::Type; + static_assert(!IsArray::value, + "MakeNotNull cannot construct an array"); + return NotNull(new Pointee(Forward(aArgs)...)); +} + // Compare two NotNulls. template inline bool diff --git a/mfbt/tests/TestNotNull.cpp b/mfbt/tests/TestNotNull.cpp index 4642dd93edd1..f295936c0668 100644 --- a/mfbt/tests/TestNotNull.cpp +++ b/mfbt/tests/TestNotNull.cpp @@ -9,10 +9,11 @@ #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" -using mozilla::WrapNotNull; +using mozilla::MakeNotNull; using mozilla::MakeUnique; using mozilla::NotNull; using mozilla::UniquePtr; +using mozilla::WrapNotNull; #define CHECK MOZ_RELEASE_ASSERT @@ -303,11 +304,72 @@ TestNotNullWithRefPtr() // and the MyRefType is destroyed. } +void +TestMakeNotNull() +{ + // Raw pointer. + auto nni = MakeNotNull(11); + static_assert(mozilla::IsSame, decltype(nni)>::value, + "MakeNotNull should return NotNull"); + CHECK(*nni == 11); + delete nni; + + // Raw pointer to const. + auto nnci = MakeNotNull(12); + static_assert(mozilla::IsSame, decltype(nnci)>::value, + "MakeNotNull should return NotNull"); + CHECK(*nnci == 12); + delete nnci; + + // Create a derived object and store its base pointer. + struct Base + { + virtual ~Base() = default; + virtual bool IsDerived() const { return false; } + }; + struct Derived : Base + { + bool IsDerived() const override { return true; } + }; + auto nnd = MakeNotNull(); + static_assert(mozilla::IsSame, decltype(nnd)>::value, + "MakeNotNull should return NotNull"); + CHECK(nnd->IsDerived()); + delete nnd; + NotNull nnb = MakeNotNull(); + static_assert(mozilla::IsSame, decltype(nnb)>::value, + "MakeNotNull should be assignable to NotNull"); + // Check that we have really built a Derived object. + CHECK(nnb->IsDerived()); + delete nnb; + + // Allow smart pointers. + auto nnmi = MakeNotNull>(23); + static_assert(mozilla::IsSame>, decltype(nnmi)>::value, + "MakeNotNull> should return NotNull>"); + CHECK(*nnmi == 23); + delete nnmi.get().get(); + + auto nnui = MakeNotNull>(24); + static_assert( + mozilla::IsSame>, decltype(nnui)>::value, + "MakeNotNull> should return NotNull>"); + CHECK(*nnui == 24); + + // Expect only 1 RefCnt (from construction). + auto nnr = MakeNotNull>(1); + static_assert( + mozilla::IsSame>, decltype(nnr)>::value, + "MakeNotNull> should return NotNull>"); + mozilla::Unused << nnr; +} + int main() { TestNotNullWithMyPtr(); TestNotNullWithRefPtr(); + TestMakeNotNull(); return 0; }