about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-02-05 04:32:06 +0000
committerbors <bors@rust-lang.org>2018-02-05 04:32:06 +0000
commit07ea2604075d6f896addce0e6949c7cf25dd3715 (patch)
tree8c2df11582867b9a627b3c73ed47ba3739a2a32c /src
parentdd29d3dd76c3aed18d7708c636b8d1e259a4c294 (diff)
parentb5f8cd5c20977119eb600b05edf699a1bde92a2c (diff)
downloadrust-07ea2604075d6f896addce0e6949c7cf25dd3715.tar.gz
rust-07ea2604075d6f896addce0e6949c7cf25dd3715.zip
Auto merge of #47873 - Aaron1011:final-ref-coerce, r=nikomatsakis
Fix ref-to-ptr coercions not working with NLL in certain cases

Implicit coercions from references to pointers were lowered to slightly
different Mir than explicit casts (e.g. 'foo as *mut T'). This resulted
in certain uses of self-referential structs compiling correctly when an
explicit cast was used, but not when the implicit coercion was used.

To fix this, this commit adds an outer 'Use' expr when applying a
raw-ptr-borrow adjustment. This makes the lowered Mir for coercions
identical to that of explicit coercions, allowing the original code to
compile regardless of how the raw ptr cast occurs.

Fixes #47722
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/hair/cx/expr.rs34
-rw-r--r--src/test/mir-opt/validate_5.rs9
-rw-r--r--src/test/run-pass/issue-47722.rs26
3 files changed, 65 insertions, 4 deletions
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 848c2d3c811..317b038c482 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -145,7 +145,39 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                     arg: expr.to_ref(),
                 },
             };
-            ExprKind::Cast { source: expr.to_ref() }
+            let cast_expr = Expr {
+                temp_lifetime,
+                ty: adjustment.target,
+                span,
+                kind: ExprKind::Cast { source: expr.to_ref() }
+            };
+
+            // To ensure that both implicit and explicit coercions are
+            // handled the same way, we insert an extra layer of indirection here.
+            // For explicit casts (e.g. 'foo as *const T'), the source of the 'Use'
+            // will be an ExprKind::Hair with the appropriate cast expression. Here,
+            // we make our Use source the generated Cast from the original coercion.
+            //
+            // In both cases, this outer 'Use' ensures that the inner 'Cast' is handled by
+            // as_operand, not by as_rvalue - causing the cast result to be stored in a temporary.
+            // Ordinary, this is identical to using the cast directly as an rvalue. However, if the
+            // source of the cast was previously borrowed as mutable, storing the cast in a
+            // temporary gives the source a chance to expire before the cast is used. For
+            // structs with a self-referential *mut ptr, this allows assignment to work as
+            // expected.
+            //
+            // For example, consider the type 'struct Foo { field: *mut Foo }',
+            // The method 'fn bar(&mut self) { self.field = self }'
+            // triggers a coercion from '&mut self' to '*mut self'. In order
+            // for the assignment to be valid, the implicit borrow
+            // of 'self' involved in the coercion needs to end before the local
+            // containing the '*mut T' is assigned to 'self.field' - otherwise,
+            // we end up trying to assign to 'self.field' while we have another mutable borrow
+            // active.
+            //
+            // We only need to worry about this kind of thing for coercions from refs to ptrs,
+            // since they get rid of a borrow implicitly.
+            ExprKind::Use { source: cast_expr.to_ref() }
         }
         Adjust::Unsize => {
             ExprKind::Unsize { source: expr.to_ref() }
diff --git a/src/test/mir-opt/validate_5.rs b/src/test/mir-opt/validate_5.rs
index c9408c1f2f8..d8d83fb5b45 100644
--- a/src/test/mir-opt/validate_5.rs
+++ b/src/test/mir-opt/validate_5.rs
@@ -52,12 +52,15 @@ fn main() {
 //         Validate(Acquire, [_1: &ReFree(DefId(0/1:9 ~ validate_5[317d]::main[0]::{{closure}}[0]), BrEnv) [closure@NodeId(46)], _2: &ReFree(DefId(0/1:9 ~ validate_5[317d]::main[0]::{{closure}}[0]), BrAnon(0)) mut i32]);
 //         StorageLive(_3);
 //         StorageLive(_4);
+//         StorageLive(_5);
 //         Validate(Suspend(ReScope(Node(ItemLocalId(9)))), [(*_2): i32]);
-//         _4 = &ReErased mut (*_2);
-//         Validate(Acquire, [(*_4): i32/ReScope(Node(ItemLocalId(9)))]);
-//         _3 = move _4 as *mut i32 (Misc);
+//         _5 = &ReErased mut (*_2);
+//         Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(9)))]);
+//         _4 = move _5 as *mut i32 (Misc);
+//         _3 = move _4;
 //         EndRegion(ReScope(Node(ItemLocalId(9))));
 //         StorageDead(_4);
+//         StorageDead(_5);
 //         Validate(Release, [_0: bool, _3: *mut i32]);
 //         _0 = const write_42(move _3) -> bb1;
 //     }
diff --git a/src/test/run-pass/issue-47722.rs b/src/test/run-pass/issue-47722.rs
new file mode 100644
index 00000000000..3b5d808e1f5
--- /dev/null
+++ b/src/test/run-pass/issue-47722.rs
@@ -0,0 +1,26 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//
+// Tests that automatic coercions from &mut T to *mut T
+// allow borrows of T to expire immediately - essentially, that
+// they work identically to 'foo as *mut T'
+#![feature(nll)]
+
+struct SelfReference {
+    self_reference: *mut SelfReference,
+}
+
+impl SelfReference {
+    fn set_self_ref(&mut self) {
+        self.self_reference = self;
+    }
+}
+
+fn main() {}