Skip to content

Commit 07ea260

Browse files
committed
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
2 parents dd29d3d + b5f8cd5 commit 07ea260

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)