Closed Bug 1335780 Opened 7 years ago Closed 7 years ago

Make Maybe<T>::emplace() work when T is const

Categories

(Core :: MFBT, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla54
Tracking Status
firefox54 --- fixed

People

(Reporter: jwatt, Assigned: jwatt)

References

(Blocks 1 open bug)

Details

Attachments

(2 files)

Right now Maybe<T>::emplace() doesn't work if T is const because the implementation of |new| can't assign the const pointer returned by mStorage.addr() to a non-const void*. |emplace| should really work in this case though since in principle we're initializing uninitialized memory.
Blocks: 1058040
I'll attach tests shortly.
Attachment #8832654 - Flags: review?(nfroyd)
This smells funny.  There are various limitations in the C++ spec surrounding const objects regarding how they can/can't be mutated, and this has the "look" of being something that's likely undefined behavior in C++.  It may well be the case that Maybe<const T> should be formally prohibited as a thing.  Some spec-wonkery seems necessary to verify that doing this is actually correct; my gut says it's not going to be, but I can't back that up with citation to spec yet.
(In reply to Jeff Walden [:Waldo] (remove +bmo to email) from comment #2)
> This smells funny.  There are various limitations in the C++ spec
> surrounding const objects regarding how they can/can't be mutated, and this
> has the "look" of being something that's likely undefined behavior in C++. 
> It may well be the case that Maybe<const T> should be formally prohibited as
> a thing.  Some spec-wonkery seems necessary to verify that doing this is
> actually correct; my gut says it's not going to be, but I can't back that up
> with citation to spec yet.

Part of the problem with this is that our documentation says that emplace() is the way to initialize Maybe<const T>:

http://dxr.mozilla.org/mozilla-central/source/mfbt/Maybe.h#473

and IRC discussion with jwatt indicates that Maybe<const T> is basically unusable, which seems very unfortunate.  AFAICT, std::optional and boost::optional don't place non-const limitations on the contained type, and we shouldn't either, so far as we are able.  At least one of the draft proposals for std::optional explicitly contains optional<const T> examples:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html#overview.usecases

That section was removed in a later draft, but I can't find any language in that draft that would prohibit optional<const T>:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3793.html#rationale.requirements

std::optional contains optional(in_place_t&, Args&&..) constructors that I think are there to let you declare Maybe objects thus:

  Maybe<const T> obj(in_place, ...);

which are an avenue worth investigating as well.  But wasn't sure how jwatt felt about implementing code like that, and if emplace() worked with a small tweak, that seemed much more reasonable in any event.
Comment on attachment 8832654 [details] [diff] [review]
part 1 - Make Maybe<T>::emplace() work when T is const

Review of attachment 8832654 [details] [diff] [review]:
-----------------------------------------------------------------

I verified the below solution works with .emplace() in a hand-written test, FWIW.  r=me with that change.

::: mfbt/Maybe.h
@@ +449,5 @@
>    template<typename... Args>
>    void emplace(Args&&... aArgs)
>    {
>      MOZ_ASSERT(!mIsSome);
> +    ::new (const_cast<typename RemoveCV<T>::Type *>(mStorage.addr())) T(Forward<Args>(aArgs)...);

I think instead of abusing const_cast here, we should just be more honest about what kind of type our storage contains:

@@ -85,7 +85,8 @@ template<class T>
 class Maybe
 {
   bool mIsSome;
-  AlignedStorage2<T> mStorage;
+  typedef typename RemoveCV<T>::Type StorageType;
+  AlignedStorage2<StorageType> mStorage;
 
 public:
   typedef T ValueType;

and then all the const-ness comes from public member functions on Maybe, so maybe that const-scariness Waldo was referring to stops being a concern.

Indeed, looking at GCC's <optional>, I see:

    private:
      // Remove const to avoid prohibition of reusing object storage for
      // const-qualified types in [3.8/9]. This is strictly internal
      // and even optional itself is oblivious to it.
      using _Stored_type = remove_const_t<_Tp>;

so we are on the right track here.
Attachment #8832654 - Flags: review?(nfroyd) → review+
Attached patch part 2 - testsSplinter Review
Attachment #8833120 - Flags: review?(nfroyd)
Comment on attachment 8833120 [details] [diff] [review]
part 2 - tests

Review of attachment 8833120 [details] [diff] [review]:
-----------------------------------------------------------------

Thanks!
Attachment #8833120 - Flags: review?(nfroyd) → review+
Pushed by jwatt@jwatt.org:
https://hg.mozilla.org/integration/mozilla-inbound/rev/a10b506ab805
part 1 - Make Maybe<T>::emplace() work when T is const. r=froydnj
https://hg.mozilla.org/integration/mozilla-inbound/rev/345fe2dd1dc9
part 2 - Tests for Maybe<const Type>. r=froydnj
Blocks: 1345840
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: