-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Consider warning when comparing wide pointers with vtables (as their address identity is unstable) #69757
Comments
Something is odd about your example, you are asserting them to be equal and then the error is that they are equal? When I run your code on playground, it works fine on nightly. But also, note that there is no guarantee that all vtables for the same type get the same address, or that they get different addresses. Vtable address identity is an unstable implementation detail (similar to, for example, address identity of functions, or of promoteable references). In other words, I see no bug here. Could you describe the expected behavior and how that differs from the actual behavior? |
@RalfJung From my reading of the OP, it seems like there's something particular about incremental compilation here, so maybe it's not reproducible on the playground? |
I guess one question is whether wide ptr equality should even compare vtables, given that they are "unstable" in a sense. I am not sure if it does, but your tests indicate they do? FWIW, the example on playground also works on stable, and in release mode. Looks like playground cannot reproduce the problem. |
Ah, I missed that incremental compilation is involved. Incremental compilation having an effect on vtable identity seems odd... I am not sure if we want to consider that a bug or not.^^ |
@jdm could you adjust your example to print not just the data address but also the vtable address? That might also explain why the output looks so strange.^^ Here's a totally not supported way to do that: fn fmt_with_vtable(ptr: *const dyn Trait) -> String {
let (ptr, vtable): (*const u8, *const u8) = unsafe { std::mem::transmute(ptr) };
format!("{:?} (vtable: {:?})", ptr, vtable)
} |
Incremental may result in different codegen unit partitioning, and each codegen unit might get its own vtable, so that part seems entirely expected to me. However the assertion failure seems to show the same address here?
|
My guess is that the main problem @jdm has is that comparing wide pointers also compares their metadata. For trait objects, this basically means equal pointers might "randomly" not be equal any more due to vtable address differences. So either @jdm could cast the raw ptr to a thin ptr in their codebase to avoid relying on vtable identities, or else comparing wide raw pointers could ignore metadata in general -- though I expect for slices we might want it to compare the length? |
Oh, right, the vtable pointer just isn't printed |
@RalfJung Do you mean that as a temporary workaround? It is definitely a regression that this broke. How codegen units are generated shouldn't influence whether two identical trait objects are indeed identical. |
Or should |
I have no idea how this never broke Servo before then.
Is there a lint somewhere to complain loudly about equality checks for unsized pointer types?
|
Given there is ample precedence for "vtable identity is unstable and allowed to change with codegen units" (and there are three open issues about it so we don't need a 4th), should this issue be turned into one of the following discussions?
|
That seems to be the worst solution (among the three you suggested I mean). In Servo, there are two different tracing objects for a given |
@nox that also sounds like the lint would have a high risk of false positives, though. |
@RalfJung Why? Our problem in Servo does not involve any generic |
It just sounded like you actually do want to compare vtables in that case? I was imagining the lint would propose to cast the pointer the |
My reply was assuming that when you said "changing pointer equality test to not compare vtables any more when comparing wide pointers" you meant to change the test in the very implementationn of If you meant in our code in Servo, yeah, we should probably do that. |
Wait didn't you just say that in Server you don't want to ignore the vtable? I am confused. Or did you want to say that Servo is being careful enough but hypothetically it would be easy to get wrong? But anyway I just take you word for it that ignoring vtables would be a bad idea. There are other arguments for this as well (not least being implementation difficulty, as for slices we likely do want to compare the metadata). |
Let's walk back a bit:
So I'm saying that we should lint against trait object comparisons, and that in Servo we should stop relying on vtable pointers being stable and change the thing I mentioned earlier. |
For the record you convinced me earlier that we need to change the code in Servo anyway because those vtable pointers were never stable, so I made the necessary changes etc in Servo. I guess this issue could be repurposed as needing a lint to report such comparisons. |
All right I edited the issue title and description accordingly. |
There is also an opposite issue: addresses of virtual tables associated with It is possible to produce such cases after repeating some optimization passes. |
Lint unnamed address comparisons Functions and vtables have an insignificant address. Attempts to compare such addresses will lead to very surprising behaviour. For example: addresses of different functions could compare equal; two trait object pointers representing the same object and the same type could be unequal. Lint against unnamed address comparisons to avoid issues like those in rust-lang/rust#69757 and rust-lang/rust#54685. Changelog: New lints: [`fn_address_comparisons`] [#5294](#5294), [`vtable_address_comparisons`] [#5294](#5294)
Lint unnamed address comparisons Functions and vtables have an insignificant address. Attempts to compare such addresses will lead to very surprising behaviour. For example: addresses of different functions could compare equal; two trait object pointers representing the same object and the same type could be unequal. Lint against unnamed address comparisons to avoid issues like those in rust-lang/rust#69757 and rust-lang/rust#54685. changelog: New lints: [`fn_address_comparisons`] [#5294](#5294), [`vtable_address_comparisons`] [#5294](#5294)
From my point of view this issue should be fixed, because it is completely not obvious that the same trait object has different fat pointers ... :( https://users.rust-lang.org/t/rwlock-hashmap-no-entry-found/42428/15 |
If we don't want to guarantee that vtables are unique, then I think a lint would need to effectively "deprecate" the I'm not sure how noisy this would end up being, or how difficult it would be to implement. |
I am not sure if rust should to have different vtables for implementation of trait for the same object I think the best that could be done is to stabilize trait object vtable address for the same object ... |
* use consistent spelling for `io::Reslut` * repplace `e.into()` with more explicit `io::Error::from` * implement From rather than Into * reduce minore code duplication by delegating to existing functions * use Arc::clone to make it explicit flow around store's identity * use buffered writer when dumping store to a file * fix latent bug around comparing wide pointers rust-lang/rust#69757. I thing we should not be hit by that due to full LTO, but that was a scary one. * remove some extra clones when working with cache
* use consistent spelling for `io::Reslut` * replace `e.into()` with more explicit `io::Error::from` * implement From rather than Into * reduce minor code duplication by delegating to existing functions * use Arc::clone to make it explicit flow around store's identity * use buffered writer when dumping store to a file * fix latent bug around comparing wide pointers rust-lang/rust#69757. I thing we should not be hit by that due to full LTO, but that was a scary one. * remove some extra clones when working with cache
* use consistent spelling for `io::Reslut` * replace `e.into()` with more explicit `io::Error::from` * implement From rather than Into * reduce minor code duplication by delegating to existing functions * use Arc::clone to make it explicit flow around store's identity * use buffered writer when dumping store to a file * fix latent bug around comparing wide pointers rust-lang/rust#69757. I thing we should not be hit by that due to full LTO, but that was a scary one. * remove some extra clones when working with cache
I think this has been closed by #117758, or is there anything left to do? |
vtable addresses may differ cross codegen units. To mitigate this, it would be good to have a lint that warns against comparing wide pointers with vtables.
Original report
This is a regression from the 2/27 to 2/28 nightly.
When this program is built with
rustc main.rs
, it runs without any trouble. When it's built withrustc main.rs -C incremental=
, I receive the following output:From the regression window, I suspect #67332.
The text was updated successfully, but these errors were encountered: