-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vtable addresses differ based on the number of codegen units #46139
Comments
The vtable pointers are different (try transmuting |
I ran into this a while ago, and @eddyb said that we never make any guarantees around uniqueness of vtables: #30485 (comment). |
@durka is there anything I can clarify? |
Something I'd like clarified - this is considered a bug right? It seems to me that If we never make any guarantees around uniqueness of vtables, then |
Not necessarily - imagine you had a
The data pointers of |
It seems pretty weird that the same trait gets different vtables. |
Okay, but to clarify again - this is a discussion of how to fix the issue? Not whether there is an issue? Because I'm appreciating the discussion of what caused it, and potential issues with simple ways to fix it, but I'm still not completely sure if people agree that it is an issue that needs fixing? To discuss the potential fix more - shouldn't they compare equal? From the documentation of Regardless, it seems important that, if there's going to be no guarantees about vtables, that doesn't also mean that there is no guarantee the comparing pointers to trait objects will ever return true. As far as I can tell, it's just not useful at that point? |
@durka Not all that weird - we could defer vtable instantiation until the final link, but then you'd have to do a bunch of rewriting of all of the rlibs you're linking to rather than just having each compilation unit create the vtables it needs. @PeterHatch The documentation should definitely note the behavior around trait objects, yeah. |
Yeah, if the behavior of comparing pointers to trait objects depends on random details of the compilation environment like CGUs, LTO, etc, it seems like a footgun for the operator to have |
|
@arielb1 states:
|
Also note that you can throw away part of the fat pointer if all you care about is the data portion: let a = container.item as *const _ as *const ();
let b = &item as *const _ as *const ();
assert!(ptr::eq(a, b)); |
Why would it be a bug? Maybe it's suboptimal? But we make no guarantees. |
It's worth giving a warning in I suppose In other words, I can cast an Should maybe
|
Some points raised in duplicates:
Also @Diggsey raises this concern
I don't think that is true. The behavior of the function is to return a copy of the argument, and I don't think we permit the mere copying of a wide ptr to change the vtable to a different but equivalent one. I don't see why the optimizer would do that, and I don't see why we would permit it in the spec. |
Devirtualization is the optimisation I was thinking of. I imagine we would eventually like to be able to compile code operating on a |
Rust doesn’t guarantee that fat pointer comparison works as expected due to duplicated vtables: rust-lang/rust#46139 Signed-off-by: Anders Kaseorg <andersk@mit.edu>
Rust doesn’t guarantee that fat pointer comparison works as expected due to duplicated vtables: rust-lang/rust#46139 Signed-off-by: Anders Kaseorg <andersk@mit.edu>
Assuming that the underlying problem is not easy to fix quickly, we should at least work around it in the standard library. I opened #80505 for |
We sometimes generate duplicate vtables depending on the number of codegen units (rust-lang#46139), so we need to exclude the vtable part when comparing fat pointers. This is safe for the case of Rc, Arc, and Weak because these always point to an entire allocation. These methods can’t be used to compare, e.g., a struct with one of its fields, a slice with one of its subslices, or consecutive ZST fields. Fixes rust-lang#63021. Signed-off-by: Anders Kaseorg <andersk@mit.edu>
Using Arc::ptr_eq on trait object pointers can fail unpredictably because of rust-lang/rust#46139. This can prevent a Hook from being removed when its RecvSelection is de-inited, which makes it incorrectly push Tokens to a queue owned by a Selector that no longer exists. This may be the cause of zesterer#44.
I think this might be a good usecase for having TypeId of non- My idea is that all vtables would have the TypeId of the (possibly non- |
I didn't see it mentioned yet (here or in the duplicates), but the ability to compare wide pointers is due to an RFC. By my reading, that RFC intends vtable pointers to be equal if and only if their types are equal; e.g. quoting the RFC:
And from the PR:
The ability landed in September 2015 (Rust 1.4), but the behavior was noted in December 2015, so this is probably an incomplete implementation as opposed to a stable-to-stable regression. (But I didn't actually do the leg-work to confirm that.) |
Can we solve this by linking the vtables with LLVM As noted by @burdges in #80505 (comment):
|
linkonce will block optimizations, while linkonce_odr is not allowed AFAICT as the different vtables contains reference different copies of the same methods in case of generics I believe and are thus not identical. Additionally I'm not sure if linkonce is supported everywhere. And finally linkonce/linkonce_odr doesn't help with dylibs. Those will still get their own copies. |
The Even if this solution is partial, is it controversial to suggest that partial solutions might be better than nothing if a full solution does not seem to be forthcoming? If nothing else, we’d generate better code in the supported cases. |
Even if they are optimized differently?
A partial solution makes people think it works without actually working IMO. |
Yes. The counterexample given in the documentation is “two functions with different semantics”, not two functions with the same semantics that are optimized differently.
This is a nondeterministic bug that depends on the way code is split between codegen units—people who would be inclined to think it works without actually working, would already think that. |
cg_clif actually deterministically generates a new vtable for every function and static using it. It also doesn't have the comdat support necessary to support linkonce. |
So how viable is this as a way to avoid false negatives when comparing Store a note: this would entirely be an implementation detail, the TypeId entry could be removed in the future if vtables are guaranteed unique. |
|
The internal intrinsic works for all types and vtables are the same independent of lifetimes anyway. |
A partial solution reduces binary size and cache pressure. What optimizations does linkonce block? Yes, dynlibs have their own copies, but maybe even these could be deduplicated somewhat. Another question: Could/should the current orphan rules be further embraced as a mechanism for deduplicating vtable methods? |
I'm seeing the ptr::eq function from the standard library sometimes return false when it should return true. This is happening in debug mode in nightly and beta, but not stable, and not in any version in release mode.
Here's the simplest I could get a reproduction of the issue:
Expected the assertion to pass, and it fails on beta and nightly, in debug mode only.
Meta
rustc +beta --version --verbose
:rustc 1.22.0-beta.3 (cc6ed06 2017-11-13)
binary: rustc
commit-hash: cc6ed06
commit-date: 2017-11-13
host: x86_64-pc-windows-msvc
release: 1.22.0-beta.3
LLVM version: 4.0
rustc +nightly --version --verbose
:rustc 1.23.0-nightly (5041b3b 2017-11-19)
binary: rustc
commit-hash: 5041b3b
commit-date: 2017-11-19
host: x86_64-pc-windows-msvc
release: 1.23.0-nightly
LLVM version: 4.0
The text was updated successfully, but these errors were encountered: