Rust: Strange Arc::ptr_eq behaviour (duplicate vtables?)

Created on 26 Jul 2019  路  10Comments  路  Source: rust-lang/rust

Hello. This issue is simmilar to https://github.com/rust-lang/rust/issues/48795 and https://github.com/rust-lang/rust/issues/46139 but have more strange behaviour and suggestion.

The problem is located when we clone Arc to Arc<Trait> in one function, return Arc and then clone Arc to Arc<Trait> in "parent" function, but Arc::ptr_eq says, that Arc<Trait>(called function) != Arc<Trait>(parent function).

Where is example of this problem, but Arc::ptr_eq works!
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=95eef8742b08ab94f3fbf3048dc514b2

We see, that data pointer is equal, but vtables are different, but implementations are correct.

In my case I have more traits, etc, project in 14k lines, and Arc::ptr_eq do not works, but this objects/traits are located in same crate. Note this problem is still occured in build --release cause, and implementations of vtables eat memory.

So..
1) Why Arc::ptr_eq match pointers to vtable, but I just need to know, points Arc to object that I search. This behaviour looks strange for user and may cause problems, when project will be large, as in my case (previously it worked correctly).
2) How should I match Arc-s?

let ptr_equal = unsafe {
                    let (a,_):(usize, usize) = std::mem::transmute_copy(&node.widget_instance);
                    let (b,_):(usize, usize) = std::mem::transmute_copy(widget);

                    a==b // Addresses of ArcInner
                };

or

```
impl WidgetInstanceTrait {
pub fn ref_eq(&self, other:&Arc) -> bool {
self.ptr_eq(&**other)
}

pub fn ptr_eq(&self, other:&WidgetTrait) -> bool {
    unsafe {
        let (a,_):(usize, usize) = std::mem::transmute_copy(&self);
        let (b,_):(usize, usize) = std::mem::transmute_copy(&other);

        a==b // Addresses of data (self)
    }
}

}

and

if node.widget_instance.ptr_eq(&**widget) { ... }
if node.widget_instance.ptr_eq(widget) { ... }

C-enhancement T-lang

Most helpful comment

The net result is C++-level foot-gunnery. The documentation there says that different implementations may compare differently, but the problem here is that the same implementation is comparing differently depending on which compilation unit converted the concrete type to a trait object.

What's worse is that this exposes an implementation detail. Let's say I have this code:

fn identity(x: Box<dyn Trait>) -> Box<dyn Trait> { x }

If the compiler is able to prove that the function is only called with Box<Foo>, then the compiler would be free to return TraitRepr { ptr: x.ptr(), vtable: MyFooVtable }, even if it was passed a different vtable.

It's difficult to see how ptr::eq could ever be used effectively on a trait object given the lack of any defined semantics?

All 10 comments

vtables are not unique. There can be, for example, a duplicate vtables in each codegen unit that needs one.

Note also that the layout of trait objects is not specified.

vtables are not unique. There can be, for example, a duplicate vtables in each codegen unit that needs one.

Seems odd to ever use the vtable pointer as part of a comparison then...

The documentation for ptr::eq covers why vtable pointers are incorporated in one of the examples: https://doc.rust-lang.org/std/ptr/fn.eq.html.

The net result is C++-level foot-gunnery. The documentation there says that different implementations may compare differently, but the problem here is that the same implementation is comparing differently depending on which compilation unit converted the concrete type to a trait object.

What's worse is that this exposes an implementation detail. Let's say I have this code:

fn identity(x: Box<dyn Trait>) -> Box<dyn Trait> { x }

If the compiler is able to prove that the function is only called with Box<Foo>, then the compiler would be free to return TraitRepr { ptr: x.ptr(), vtable: MyFooVtable }, even if it was passed a different vtable.

It's difficult to see how ptr::eq could ever be used effectively on a trait object given the lack of any defined semantics?

Relabeling as T-lang as this does seem like a bit of an unfortunate result of compiler internals, ideally we'd not create different vtables for different codegen units. Maybe there's some sort of linker or LLVM attribute we could set to deduplicate those?

Hi. We've just ran into this problem (cf. the bug referenced), it seems that, for example, turning off/on incremental compilation is enough to trigger a difference. This is definitely a footgun.

IMO at least the documentation should be updated, it doesn't seem to mention this problem and the documentation for Arc::ptr_eq seems to be simply wrong. On a more positive note, I'm willing to help if need be :)

To avoid having many issues open that are all about the same thing, should this be closed in favor of https://github.com/rust-lang/rust/issues/46139 or https://github.com/rust-lang/rust/issues/48795 or https://github.com/rust-lang/rust/issues/69757?

@RalfJung IMO #48795 describes the problem most succintly / without imvolving other not-so-imporant specifics. (So, yeah.)

Okay let's close it... but I feel https://github.com/rust-lang/rust/issues/46139 actually is more to the point.

Closing as duplicate of https://github.com/rust-lang/rust/issues/46139.

Was this page helpful?
0 / 5 - 0 ratings