Skip to content

unsoundness caused by hidden borrow in nightly match ergonomics #49631

Closed
@bvinc

Description

@bvinc

Playground

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Foo {
    ReallyReallyReallyLongName,
    Foo2,
    Foo3,
}

struct Iter {
    done: bool
}

impl Iterator for Iter {
    type Item = Result<Foo, String>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.done { return None; }
        self.done = true;
        Some(Ok(Foo::ReallyReallyReallyLongName))
    }
}

fn main() {
    let iter = Iter{done: false};
    let mut iter = iter.peekable();
    
    // peek() returns type Option<&Result<Foo, String>>
    // pattern Some(Ok(foo)) is used instead of Some(&Ok(foo))
    // This causes foo to be a hidden borrow of type &Foo
    while let Some(Ok(foo)) = iter.peek() {
        iter.next();  // destroys what foo is pointing to
        println!("foo={:?}", *foo);
    }
}

Output:

Illegal instruction (core dumped)

The above code should fail the borrow checker. They key line seems to be the pattern match. It creates a borrow that seems to be invisible to the borrow checker. This appears to only be possible in nightly due to the match_default_bindings feature.

Metadata

Metadata

Assignees

Labels

I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-betaPerformance or correctness regression from stable to beta.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions