Skip to content

Consider warning when comparing wide pointers with vtables (as their address identity is unstable) #69757

Open
@jdm

Description

@jdm
Contributor

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.

pub trait Trait {}
impl Trait for i32 {}

pub struct Obj<'a, T: 'static + Trait> {
    ptr: &'a T,
    trait_ptr: *const dyn Trait,
}

impl<'a, T: Trait + 'static> Drop for Obj<'a, T> {
    fn drop(&mut self) {
        assert_eq!(self.trait_ptr, self.ptr as *const dyn Trait);
    }
}

fn main() {
    let ptr = 5;
    let _name = Obj {
        ptr: &ptr,
        trait_ptr: &ptr,
    };
}

When this program is built with rustc main.rs, it runs without any trouble. When it's built with rustc main.rs -C incremental=, I receive the following output:

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `0x7ffee7d23864`,
 right: `0x7ffee7d23864`', main.rs:11:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

From the regression window, I suspect #67332.

Activity

changed the title [-]Different vtables for trait object pointers in debug builds[/-] [+]Different vtables for trait object pointers in incremental builds[/+] on Mar 6, 2020
RalfJung

RalfJung commented on Mar 6, 2020

@RalfJung
Member

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?

added
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
T-langRelevant to the language team
on Mar 6, 2020
Centril

Centril commented on Mar 6, 2020

@Centril
Contributor

@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?

RalfJung

RalfJung commented on Mar 6, 2020

@RalfJung
Member

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.

RalfJung

RalfJung commented on Mar 6, 2020

@RalfJung
Member

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

RalfJung

RalfJung commented on Mar 6, 2020

@RalfJung
Member

@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.^^
(You seem to have already diagnosed that this is about different vtable pointers, but do not explain how you arrived at that conclusion?)

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)
}
jonas-schievink

jonas-schievink commented on Mar 6, 2020

@jonas-schievink
Contributor

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?

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `0x7ffee7d23864`,
 right: `0x7ffee7d23864`', main.rs:11:9
RalfJung

RalfJung commented on Mar 6, 2020

@RalfJung
Member

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?

jonas-schievink

jonas-schievink commented on Mar 6, 2020

@jonas-schievink
Contributor

Oh, right, the vtable pointer just isn't printed

nox

nox commented on Mar 6, 2020

@nox
Contributor

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

41 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.A-trait-systemArea: Trait systemT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jdm@nox@RalfJung@Centril@Aaron1011

        Issue actions

          Consider warning when comparing wide pointers with vtables (as their address identity is unstable) · Issue #69757 · rust-lang/rust