Skip to content

Adding or removing an item should not silently change behavior of downstream code #11878

Closed
@glaebhoerl

Description

@glaebhoerl

If you add or remove a trait impl, or anonymous impl, or any other item in upstream code, this should have one of three effects on downstream code:

  1. Nothing.
  2. Where it previously compiled, now it fails to.
  3. Where it previously failed to compile, now it succeeds.

What should not happen is

   4. It continues to compile, but its runtime behavior is now different.

This property is currently known to be violated by method call autoderef combined with trait impls on reference types (#11818). For instance:

mod up1 {
    pub trait Stringify {
        fn stringify(&self) -> ~str;
    }
    impl<'a, T> Stringify for &'a T {
        fn stringify(&self) -> ~str { ~"Hi, I'm a borrowed reference!" }
    }
}

mod up2 {
    use up1::Stringify;
    pub struct Foo;

    #[cfg(with_foo_impl)]
    impl Stringify for Foo {
        fn stringify(&self) -> ~str { ~"This is Foo" }
    }
}

mod down {
    use up1::Stringify;
    use up2::Foo;
    pub fn some_string() -> ~str {
        (&Foo).stringify()
    }
}

fn main() {
    println!("{}", down::some_string())
}

When compiled with --cfg with_foo_impl, this code will output "This is Foo". Otherwise it will say "Hi, I'm a borrowed reference!".

Why this is important: it seriously impairs the ability to reason about interface stability and compatibility. In today's situation, upstreams cannot add or remove impls for their types without having to worry about introducing silent breakage somewhere downstream. If this property held, then the worst case would be that downstream would be alerted to the change by a compile failure, which is much, much preferable to silent breakage. (This is exactly the kind of thing static type systems are supposed to ensure!)

The provided example is only one instance I know of where the principle is violated, I don't know whether there might be others.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions