Skip to content

Fully-qualifying a From::from call adds an implicit reborrow that is not inferred otherwise #89966

Open
@camelid

Description

@camelid

This code fails with a borrow-check error:

struct Foo;

impl<'a> From<&'a mut Foo> for () {
    fn from(_: &'a mut Foo) -> () {}
}

fn main() {
    let mut foo = Foo;
    let foo_mut = &mut foo;

    <()>::from(foo_mut);
    <()>::from(foo_mut);
}
error[E0382]: use of moved value: `foo_mut`
  --> src/main.rs:12:16
   |
9  |     let foo_mut = &mut foo;
   |         ------- move occurs because `foo_mut` has type `&mut Foo`, which does not implement the `Copy` trait
10 | 
11 |     <()>::from(foo_mut);
   |                ------- value moved here
12 |     <()>::from(foo_mut);
   |                ^^^^^^^ value used here after move

For more information about this error, try `rustc --explain E0382`.

However, explicitly specifying <() as From<&mut Foo>> in the first call causes compilation to succeed:

struct Foo;

impl<'a> From<&'a mut Foo> for () {
    fn from(_: &'a mut Foo) -> () {}
}

fn main() {
    let mut foo = Foo;
    let foo_mut = &mut foo;

    <() as From<&mut Foo>>::from(foo_mut);
    <()>::from(foo_mut);
}

Comparing the MIR output for the two versions, it appears that the fully-qualified version reborrows from foo_mut, whereas the unqualified version does not:

     bb0: {
-        StorageLive(_1);                 // scope 0 at unqual.rs:10:9: 10:16
-        _1 = Foo;                        // scope 0 at unqual.rs:10:19: 10:22
-        FakeRead(ForLet(None), _1);      // scope 0 at unqual.rs:10:9: 10:16
-        StorageLive(_2);                 // scope 1 at unqual.rs:11:9: 11:16
-        _2 = &mut _1;                    // scope 1 at unqual.rs:11:19: 11:27
-        FakeRead(ForLet(None), _2);      // scope 1 at unqual.rs:11:9: 11:16
-        StorageLive(_3);                 // scope 2 at unqual.rs:13:5: 13:24
-        StorageLive(_4);                 // scope 2 at unqual.rs:13:16: 13:23
-        _4 = move _2;                    // scope 2 at unqual.rs:13:16: 13:23
-        _3 = <() as From<&mut Foo>>::from(move _4) -> [return: bb1, unwind: bb3]; // scope 2 at unqual.rs:13:5: 13:24
+        StorageLive(_1);                 // scope 0 at fqs.rs:10:9: 10:16
+        _1 = Foo;                        // scope 0 at fqs.rs:10:19: 10:22
+        FakeRead(ForLet(None), _1);      // scope 0 at fqs.rs:10:9: 10:16
+        StorageLive(_2);                 // scope 1 at fqs.rs:11:9: 11:16
+        _2 = &mut _1;                    // scope 1 at fqs.rs:11:19: 11:27
+        FakeRead(ForLet(None), _2);      // scope 1 at fqs.rs:11:9: 11:16
+        StorageLive(_3);                 // scope 2 at fqs.rs:13:5: 13:42
+        StorageLive(_4);                 // scope 2 at fqs.rs:13:34: 13:41
+        _4 = &mut (*_2);                 // scope 2 at fqs.rs:13:34: 13:41
+        _3 = <() as From<&mut Foo>>::from(move _4) -> [return: bb1, unwind: bb3]; // scope 2 at fqs.rs:13:5: 13:42
                                          // mir::Constant
-                                         // + span: unqual.rs:13:5: 13:15
+                                         // + span: fqs.rs:13:5: 13:33
                                          // + user_ty: UserType(0)
                                          // + literal: Const { ty: fn(&mut Foo) {<() as std::convert::From<&mut Foo>>::from}, val: Value(Scalar(<ZST>)) }
     }

I'm guessing this reborrow is due to, or at least related to, the following change in "User Type Annotations":

 | User Type Annotations
-| 0: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: TypeOf(DefId(2:2912 ~ core[f488]::convert::From::from), UserSubsts { substs: [(), ^0], user_self_ty: None }) } at unqual.rs:13:5: 13:15
-| 1: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: TypeOf(DefId(2:2912 ~ core[f488]::convert::From::from), UserSubsts { substs: [(), ^0], user_self_ty: None }) } at unqual.rs:14:5: 14:15
+| 0: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Region(U0) }], value: TypeOf(DefId(2:2912 ~ core[f488]::convert::From::from), UserSubsts { substs: [(), &mut Foo], user_self_ty: None }) } at fqs.rs:13:5: 13:33
+| 1: Canonical { max_universe: U0, variables: [CanonicalVarInfo { kind: Ty(General(U0)) }], value: TypeOf(DefId(2:2912 ~ core[f488]::convert::From::from), UserSubsts { substs: [(), ^0], user_self_ty: None }) } at fqs.rs:14:5: 14:15

In the unqualified <()>::from(...) version, From::from's type seems to be generic over a type parameter, while in the qualified <() as From<&mut Foo>>::from(...) version, From::from's type is generic over a lifetime.

See also this Zulip thread.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerA-inferenceArea: Type inferenceA-type-systemArea: Type systemC-discussionCategory: Discussion or questions that doesn't represent real issues.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language teamT-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions