Stream: general

Topic: Unsized coercion appears twice in MIR?


RalfJ (Dec 11 2019 at 15:31, on Zulip):

To my surprise, the following code

use std::rc::Rc;
fn foo(x: Rc<String>) -> Rc<dyn Send> { x }

generates MIR like this:

fn  foo(_1: std::rc::Rc<std::string::String>) -> std::rc::Rc<dyn std::marker::Send> {
    let mut _0: std::rc::Rc<dyn std::marker::Send>; // return place in scope 0 at src/lib.rs:2:26: 2:38
    let mut _2: std::rc::Rc<dyn std::marker::Send>; // in scope 0 at src/lib.rs:2:41: 2:42
    let mut _3: std::rc::Rc<std::string::String>; // in scope 0 at src/lib.rs:2:41: 2:42

    bb0: {
        StorageLive(_2);                 // bb0[0]: scope 0 at src/lib.rs:2:41: 2:42
        StorageLive(_3);                 // bb0[1]: scope 0 at src/lib.rs:2:41: 2:42
        _3 = move _1;                    // bb0[2]: scope 0 at src/lib.rs:2:41: 2:42
        _2 = move _3 as std::rc::Rc<dyn std::marker::Send> (Pointer(Unsize)); // bb0[3]: scope 0 at src/lib.rs:2:41: 2:42
        StorageDead(_3);                 // bb0[4]: scope 0 at src/lib.rs:2:41: 2:42
        _0 = move _2 as std::rc::Rc<dyn std::marker::Send> (Pointer(Unsize)); // bb0[5]: scope 0 at src/lib.rs:2:41: 2:42
        StorageDead(_2);                 // bb0[6]: scope 0 at src/lib.rs:2:43: 2:44
        return;                          // bb0[7]: scope 0 at src/lib.rs:2:44: 2:44
    }
}

why does it cast twice? Cc @oli @eddyb

simulacrum (Dec 11 2019 at 15:39, on Zulip):

hm, I wonder if we unsize whenever we move an unsized value

RalfJ (Dec 11 2019 at 15:40, on Zulip):

Rc<dyn Send> isn't an unsized value though

simulacrum (Dec 11 2019 at 15:41, on Zulip):

er, yeah, poor phrasing on my part unsized "type" or so, not sure if we have a good name

simulacrum (Dec 11 2019 at 15:41, on Zulip):

e.g. fn foo(x: Rc<String>) -> Rc<dyn Send> { let v: Rc<dyn Send> = x; v } produces:

fn  foo(_1: std::rc::Rc<std::string::String>) -> std::rc::Rc<dyn std::marker::Send> {
    debug x => _1;                       // in scope 0 at src/lib.rs:2:8: 2:9
    let mut _0: std::rc::Rc<dyn std::marker::Send>; // return place in scope 0 at src/lib.rs:2:26: 2:38
    let mut _2: std::rc::Rc<dyn std::marker::Send>; // in scope 0 at src/lib.rs:2:66: 2:67
    let _3: std::rc::Rc<dyn std::marker::Send> as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 0 at src/lib.rs:2:45: 2:46
    let mut _4: std::rc::Rc<std::string::String>; // in scope 0 at src/lib.rs:2:63: 2:64
    let mut _5: std::rc::Rc<dyn std::marker::Send>; // in scope 0 at src/lib.rs:2:66: 2:67
    scope 1 {
        debug v => _3;                   // in scope 1 at src/lib.rs:2:45: 2:46
    }

    bb0: {
        StorageLive(_2);                 // bb0[0]: scope 0 at src/lib.rs:2:66: 2:67
        StorageLive(_3);                 // bb0[1]: scope 0 at src/lib.rs:2:45: 2:46
        StorageLive(_4);                 // bb0[2]: scope 0 at src/lib.rs:2:63: 2:64
        _4 = move _1;                    // bb0[3]: scope 0 at src/lib.rs:2:63: 2:64
        _3 = move _4 as std::rc::Rc<dyn std::marker::Send> (Pointer(Unsize)); // bb0[4]: scope 0 at src/lib.rs:2:63: 2:64
        StorageDead(_4);                 // bb0[5]: scope 0 at src/lib.rs:2:63: 2:64
        StorageLive(_5);                 // bb0[6]: scope 1 at src/lib.rs:2:66: 2:67
        _5 = move _3;                    // bb0[7]: scope 1 at src/lib.rs:2:66: 2:67
        _2 = move _5 as std::rc::Rc<dyn std::marker::Send> (Pointer(Unsize)); // bb0[8]: scope 1 at src/lib.rs:2:66: 2:67
        StorageDead(_5);                 // bb0[9]: scope 1 at src/lib.rs:2:66: 2:67
        StorageDead(_3);                 // bb0[10]: scope 0 at src/lib.rs:2:68: 2:69
        _0 = move _2 as std::rc::Rc<dyn std::marker::Send> (Pointer(Unsize)); // bb0[11]: scope 0 at src/lib.rs:2:66: 2:67
        StorageDead(_2);                 // bb0[12]: scope 0 at src/lib.rs:2:68: 2:69
        return;                          // bb0[13]: scope 0 at src/lib.rs:2:69: 2:69
    }
}
simulacrum (Dec 11 2019 at 15:42, on Zulip):

which casts another time

Jake Goulding (Dec 11 2019 at 16:06, on Zulip):

Clearly this is a job for the MIR optimization work :-)

eddyb (Dec 11 2019 at 16:19, on Zulip):

lmao

eddyb (Dec 11 2019 at 16:20, on Zulip):

@RalfJ this is Rc<T> -> Rc<dyn Trait> -> Rc<dyn Trait>

eddyb (Dec 11 2019 at 16:20, on Zulip):

the second one is a noop

eddyb (Dec 11 2019 at 16:21, on Zulip):

try fn foo(x: Rc<dyn Send>) -> Rc<dyn Send> { x }

eddyb (Dec 11 2019 at 16:21, on Zulip):

the problem is that this might actually be needed by borrowck in some cases hmpf

eddyb (Dec 11 2019 at 16:21, on Zulip):

cc @Matthew Jasper

Matthew Jasper (Dec 11 2019 at 17:15, on Zulip):

It's needed in borrowck because &mut Send + 'static -> &mut Send + 'a is a valid coercion, even though &mut T is invariant in T. It does seem a bit excessive to generate two casts in the case in the OP though.

eddyb (Dec 12 2019 at 12:47, on Zulip):

@Matthew Jasper maybe we can detect the case where it's 'static -> 'static?

eddyb (Dec 12 2019 at 12:47, on Zulip):

like in the OP

Last update: Jan 21 2020 at 08:35UTC