Skip to content

Copy propagation on "move" assignments introduces UB (using Miri/MiniRust semantics) #556

Open
@RalfJung

Description

@RalfJung

So far, we have a theory for what move could mean on function calls, but we don't have a clear idea what it could mean in assignments (#416). Turns out the two are tightly linked: this example compiles to the following MIR without optimizations:

fn src(_1: Foo) -> () {
    debug x => _1;
    let mut _0: ();
    let _2: ();
    let _3: &Foo;
    let _4: ();

    bb0: {
        _3 = &_1;
        _2 = escape(copy _3) -> [return: bb1, unwind continue];
    }

    bb1: {
        _4 = move_arg(move _1) -> [return: bb2, unwind continue];
    }

    bb2: {
        return;
    }
}

And the following with optimizations:

fn src(_1: Foo) -> () {
    debug x => _1;
    let mut _0: ();
    let _2: ();
    let mut _3: Foo;
    scope 1 (inlined escape) {
        let mut _4: *const Foo;
        let mut _5: *mut *const Foo;
    }

    bb0: {
        _4 = &raw const _1;
        StorageLive(_5);
        _5 = const {alloc1: *mut *const Foo};
        (*_5) = copy _4;
        StorageDead(_5);
        StorageLive(_3);
        _3 = move _1;
        _2 = move_arg(move _3) -> [return: bb1, unwind continue];
    }

    bb1: {
        StorageDead(_3);
        return;
    }
}

Miri semantics says the former program has UB (though I have not managed to actually executed that MIR in Miri, passing -O is not enough somehow), while the latter is fine. The latter is fine since _3 = move _1; ignores the move, and so the argument actually moved to move_arg is _3 and that argument gets protected for the duration of the call.

I assume we want to allow the transformation that removes the extra move (though I don't know if we currently perform such an optimization); that is very tricky: _1 might be accessed in various ways after the move (even if we consider a move to de-init memory, one can still write to it); if the argument moved to move_arg becomes an alias of _1 that can easily introduce conflicts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions