Bug 1884457 Comment 8 Edit History

Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.

(In reply to Steve Fink [:sfink] [:s:] from comment #7)
> My guess:
> 
> ```
> MarkStack& MarkStack::operator=(const MarkStack& other) {
>   new (this) MarkStack(other);
>   return *this;
> }
> ```
> is not a problem when `this == &other` because this would call
> ```
> MarkStack::MarkStack(const MarkStack& other) {
>   MOZ_CRASH("Compiler requires this but doesn't call it");
> }

Good point!
> ```
> MarkStack& MarkStack::operator=(MarkStack&& other) noexcept {
>   new (this) MarkStack(std::move(other));
>   return *this;
> }
>````
> on the other hand, looks like a problem if `this == &other`, because it *does* read from `other`:
> ```
> MarkStack::MarkStack(MarkStack&& other) noexcept
>     : stack_(std::move(other.stack_.ref())),
>       topIndex_(other.topIndex_.ref())
> #ifdef JS_GC_ZEAL
>       ,
>       maxCapacity_(other.maxCapacity_)
> #endif
> {
>   other.topIndex_ = 0;
> }
> ```
> I don't know what the specified order of operations is here—my guess is that it evaluates `std::move(other)`, then "blesses" the memory as an uninitialized `MarkStack` object,

I'm pretty sure it's just uninitialized memory, having no object identity at that point, as it's "lifetime" (in spec-ese) has ended. As https://eel.is/c++draft/basic.life s.6.7.3(4) says: `The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime.`

> and then invokes the constructor.

At which point the uninitialized memory becomes again a `MarkStack` object.

> Which would indeed read from blessed but indeterminate memory. (Sorry, I haven't looked up spec terms for any of this.)

Yep.
(In reply to Steve Fink [:sfink] [:s:] from comment #7)
> My guess:
> 
> ```
> MarkStack& MarkStack::operator=(const MarkStack& other) {
>   new (this) MarkStack(other);
>   return *this;
> }
> ```
> is not a problem when `this == &other` because this would call
> ```
> MarkStack::MarkStack(const MarkStack& other) {
>   MOZ_CRASH("Compiler requires this but doesn't call it");
> }

Good point!
> ```
> MarkStack& MarkStack::operator=(MarkStack&& other) noexcept {
>   new (this) MarkStack(std::move(other));
>   return *this;
> }
>````
> on the other hand, looks like a problem if `this == &other`, because it *does* read from `other`:
> ```
> MarkStack::MarkStack(MarkStack&& other) noexcept
>     : stack_(std::move(other.stack_.ref())),
>       topIndex_(other.topIndex_.ref())
> #ifdef JS_GC_ZEAL
>       ,
>       maxCapacity_(other.maxCapacity_)
> #endif
> {
>   other.topIndex_ = 0;
> }
> ```
> I don't know what the specified order of operations is here—my guess is that it evaluates `std::move(other)`, then "blesses" the memory as an uninitialized `MarkStack` object,

I'm pretty sure it's just uninitialized memory, having no object identity at that point, as its "lifetime" (in spec-ese) has ended. As https://eel.is/c++draft/basic.life s.6.7.3(4) says: `The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime.`

> and then invokes the constructor.

At which point the uninitialized memory becomes again a `MarkStack` object.

> Which would indeed read from blessed but indeterminate memory. (Sorry, I haven't looked up spec terms for any of this.)

Yep.

Back to Bug 1884457 Comment 8