diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 848c2d3c811e99495a83aade112bffb9a4b100c6..317b038c4829566bdddd7e9189553b60ca5e4431 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 c9408c1f2f88b5de1392912e0341ec1b81b10cd0..d8d83fb5b453707c69d2c8b46de919495d893358 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 0000000000000000000000000000000000000000..3b5d808e1f54686a80f3301baa4323767257e5a5 --- /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 or the MIT license +// , 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() {}