Skip to content

Rust 1.84 sometimes allows overlapping impls in incremental re-builds #135514

Closed
@steffahn

Description

This repro uses a bit of lexical comments trickery, but only for convenience. (Quite useful while manually testing multiple rust versions), so the whole change you need to do between incremental compilations is turning

// /* // <- uncomment this line

into

/* // <- uncomment this line

The main relevant change that this entails is

impl Trait for W {}

turning into

impl Trait for S<W> {}

which makes the Other-impls overlapping. As an additional effect, the code turning the overlap into UB is also uncommented. (The const _ … stuff only “fixes” the * symbols left behind by the */* in the middle.)

trait Trait {}

struct S0<T>(T);

struct S<T>(T);
impl<T> Trait for S<T> where S0<T>: Trait {}

struct W;

trait Other {
    type Choose<L, R>;
}

struct A;
struct B;

// first impl
impl<T: Trait> Other for T {
    type Choose<L, R> = L;
}

// second impl
impl<T> Other for S<T> {
    type Choose<L, R> = R;
}

const _: u8 = 0

// /* // <- uncomment this line

*0;

impl Trait for W {}

pub fn transmute<L, R>(l: L) -> R {
    todo!();
}

const _: u8 = 0
*/*
0;

impl Trait for S<W> {}

fn use_first_impl<T: Trait, L, R>(l: L) -> <<T as TyEq>::To as Other>::Choose<L, R> {
    l
}

fn use_second_impl<T, L, R>(l: <S<T> as Other>::Choose<L, R>) -> R {
    l
}

trait TyEq {
    type To;
}
impl<T> TyEq for T {
    type To = T;
}

fn transmute_inner<W, T, L, R>(l: L) -> R
where
    T: Trait + TyEq<To = S<W>>,
{
    use_second_impl::<W, L, R>(use_first_impl::<T, L, R>(l))
}

pub fn transmute<L, R>(l: L) -> R {
    transmute_inner::<W, S<W>, L, R>(l)
}

const _: u8 =
// */
0;

fn main() {
    let v = vec![65_u8, 66, 67];
    let s: String = transmute(v);
    println!("{}", s);
}

Reproduce

cargo new repro
cd repro
…write above to src/main…
cargo run
…uncomment the line in question as described…
cargo run
   Compiling repro v0.1.0 (/home/frank/repro)
warning: struct `A` is never constructed
  --> src/main.rs:14:8
   |
14 | struct A;
   |        ^
   |
   = note: `#[warn(dead_code)]` on by default

warning: struct `B` is never constructed
  --> src/main.rs:15:8
   |
15 | struct B;
   |        ^

warning: `repro` (bin "repro") generated 2 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `target/debug/repro`
ABC

Is safe code that compiles to UB, but only in incremental re-builds (compilation error otherwise), considered a soundness issue to be labelled I-unsound?

This was already fixed with #133828 (which also explains what was the underlying issue). That PR seems like a fairly small & straightforward fix to me… should it perhaps be considered for backporting to stable? cc @compiler-errors

@rustbot label regression-from-stable-to-stable, T-compiler, A-incr-comp, A-coherence

Metadata

Assignees

No one assigned

    Labels

    A-coherenceArea: CoherenceA-incr-compArea: Incremental compilationC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-lowLow priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions