(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.
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 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.