Skip to content

Commit b5f8cd5

Browse files
committed
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 rust-lang#47722
1 parent 90eb44a commit b5f8cd5

File tree

3 files changed

+65
-4
lines changed

3 files changed

+65
-4
lines changed

src/librustc_mir/hair/cx/expr.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,39 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
145145
arg: expr.to_ref(),
146146
},
147147
};
148-
ExprKind::Cast { source: expr.to_ref() }
148+
let cast_expr = Expr {
149+
temp_lifetime,
150+
ty: adjustment.target,
151+
span,
152+
kind: ExprKind::Cast { source: expr.to_ref() }
153+
};
154+
155+
// To ensure that both implicit and explicit coercions are
156+
// handled the same way, we insert an extra layer of indirection here.
157+
// For explicit casts (e.g. 'foo as *const T'), the source of the 'Use'
158+
// will be an ExprKind::Hair with the appropriate cast expression. Here,
159+
// we make our Use source the generated Cast from the original coercion.
160+
//
161+
// In both cases, this outer 'Use' ensures that the inner 'Cast' is handled by
162+
// as_operand, not by as_rvalue - causing the cast result to be stored in a temporary.
163+
// Ordinary, this is identical to using the cast directly as an rvalue. However, if the
164+
// source of the cast was previously borrowed as mutable, storing the cast in a
165+
// temporary gives the source a chance to expire before the cast is used. For
166+
// structs with a self-referential *mut ptr, this allows assignment to work as
167+
// expected.
168+
//
169+
// For example, consider the type 'struct Foo { field: *mut Foo }',
170+
// The method 'fn bar(&mut self) { self.field = self }'
171+
// triggers a coercion from '&mut self' to '*mut self'. In order
172+
// for the assignment to be valid, the implicit borrow
173+
// of 'self' involved in the coercion needs to end before the local
174+
// containing the '*mut T' is assigned to 'self.field' - otherwise,
175+
// we end up trying to assign to 'self.field' while we have another mutable borrow
176+
// active.
177+
//
178+
// We only need to worry about this kind of thing for coercions from refs to ptrs,
179+
// since they get rid of a borrow implicitly.
180+
ExprKind::Use { source: cast_expr.to_ref() }
149181
}
150182
Adjust::Unsize => {
151183
ExprKind::Unsize { source: expr.to_ref() }

src/test/mir-opt/validate_5.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,15 @@ fn main() {
5252
// 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]);
5353
// StorageLive(_3);
5454
// StorageLive(_4);
55+
// StorageLive(_5);
5556
// Validate(Suspend(ReScope(Node(ItemLocalId(9)))), [(*_2): i32]);
56-
// _4 = &ReErased mut (*_2);
57-
// Validate(Acquire, [(*_4): i32/ReScope(Node(ItemLocalId(9)))]);
58-
// _3 = move _4 as *mut i32 (Misc);
57+
// _5 = &ReErased mut (*_2);
58+
// Validate(Acquire, [(*_5): i32/ReScope(Node(ItemLocalId(9)))]);
59+
// _4 = move _5 as *mut i32 (Misc);
60+
// _3 = move _4;
5961
// EndRegion(ReScope(Node(ItemLocalId(9))));
6062
// StorageDead(_4);
63+
// StorageDead(_5);
6164
// Validate(Release, [_0: bool, _3: *mut i32]);
6265
// _0 = const write_42(move _3) -> bb1;
6366
// }

src/test/run-pass/issue-47722.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// Tests that automatic coercions from &mut T to *mut T
12+
// allow borrows of T to expire immediately - essentially, that
13+
// they work identically to 'foo as *mut T'
14+
#![feature(nll)]
15+
16+
struct SelfReference {
17+
self_reference: *mut SelfReference,
18+
}
19+
20+
impl SelfReference {
21+
fn set_self_ref(&mut self) {
22+
self.self_reference = self;
23+
}
24+
}
25+
26+
fn main() {}

0 commit comments

Comments
 (0)