Skip to content

Commit 7f8d245

Browse files
committed
fix coercion behavior for nested references
1 parent cdaee4a commit 7f8d245

File tree

2 files changed

+85
-8
lines changed

2 files changed

+85
-8
lines changed

src/librustc_typeck/check/coercion.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
230230

231231
let lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
232232
let mut first_error = None;
233+
let mut r_borrow_var = None;
233234
let (_, autoderefs, success) = autoderef(self.fcx, span, a, exprs,
234235
UnresolvedTypeAction::Ignore,
235236
lvalue_pref,
@@ -264,21 +265,57 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
264265
// mutability [1], since it may be that we are coercing
265266
// from `&mut T` to `&U`.
266267
//
267-
// One fine point concerns the region that we use [2]. We
268+
// One fine point concerns the region that we use. We
268269
// choose the region such that the region of the final
269270
// type that results from `unify` will be the region we
270271
// want for the autoref:
271272
//
272-
// - if in lub mode, that means we want to unify `&'a mut [T]`
273-
// (from source) and `&'b mut [T]` (target).
274-
// - if in sub mode, that means we want to use `'b` for
275-
// both pointers. This is because sub mode (somewhat
273+
// - if in sub mode, that means we want to use `'b` (the
274+
// region from the target reference) for both
275+
// pointers [2]. This is because sub mode (somewhat
276276
// arbitrarily) returns the subtype region. In the case
277277
// where we are coercing to a target type, we know we
278278
// want to use that target type region (`'b`) because --
279279
// for the program to type-check -- it must be the
280280
// smaller of the two.
281-
let r = if self.use_lub {r_a} else {r_b}; // [2] above
281+
// - if in lub mode, things can get fairly complicated. The
282+
// easiest thing is just to make a fresh
283+
// region variable [4], which effectively means we defer
284+
// the decision to region inference (and regionck, which will add
285+
// some more edges to this variable). However, this can wind up
286+
// creating a crippling number of variables in some cases --
287+
// e.g. #32278 -- so we optimize one particular case [3].
288+
// Let me try to explain with some examples:
289+
// - The "running example" above represents the simple case,
290+
// where we have one `&` reference at the outer level and
291+
// ownership all the rest of the way down. In this case,
292+
// we want `LUB('a, 'b)` as the resulting region.
293+
// - However, if there are nested borrows, that region is
294+
// too strong. Consider a coercion from `&'a &'x Rc<T>` to
295+
// `&'b T`. In this case, `'a` is actually irrelevant.
296+
// The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)`
297+
// we get spurious errors (`run-pass/regions-lub-ref-ref-rc.rs`).
298+
// (The errors actually show up in borrowck, typically, because
299+
// this extra edge causes the region `'a` to be inferred to something
300+
// too big, which then results in borrowck errors.)
301+
// - We could track the innermost shared reference, but there is already
302+
// code in regionck that has the job of creating links between
303+
// the region of a borrow and the regions in the thing being
304+
// borrowed (here, `'a` and `'x`), and it knows how to handle
305+
// all the various cases. So instead we just make a region variable
306+
// and let regionck figure it out.
307+
let r = if !self.use_lub {
308+
r_b // [2] above
309+
} else if autoderef == 1 {
310+
r_a // [3] above
311+
} else {
312+
if r_borrow_var.is_none() { // create var lazilly, at most once
313+
let coercion = Coercion(span);
314+
let r = self.fcx.infcx().next_region_var(coercion);
315+
r_borrow_var = Some(self.tcx().mk_region(r)); // [4] above
316+
}
317+
r_borrow_var.unwrap()
318+
};
282319
let derefd_ty_a = self.tcx().mk_ref(r, TypeAndMut {
283320
ty: referent_ty,
284321
mutbl: mt_b.mutbl // [1] above
@@ -302,18 +339,22 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
302339
let ty = match success {
303340
Some(ty) => ty,
304341
None => {
305-
return Err(first_error.expect("coerce_borrowed_pointer had no error"));
342+
let err = first_error.expect("coerce_borrowed_pointer had no error");
343+
debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
344+
return Err(err);
306345
}
307346
};
308347

309348
// Now apply the autoref. We have to extract the region out of
310349
// the final ref type we got.
311350
let r_borrow = match ty.sty {
312-
ty::TyRef(r, _) => r,
351+
ty::TyRef(r_borrow, _) => r_borrow,
313352
_ => self.tcx().sess.span_bug(span,
314353
&format!("expected a ref type, got {:?}", ty))
315354
};
316355
let autoref = Some(AutoPtr(r_borrow, mt_b.mutbl));
356+
debug!("coerce_borrowed_pointer: succeeded ty={:?} autoderefs={:?} autoref={:?}",
357+
ty, autoderefs, autoref);
317358
Ok((ty, AdjustDerefRef(AutoDerefRef {
318359
autoderefs: autoderefs,
319360
autoref: autoref,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2012 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+
// Test a corner case of LUB coercion. In this case, one arm of the
12+
// match requires a deref coercion and other other doesn't, and there
13+
// is an extra `&` on the `rc`. We want to be sure that the lifetime
14+
// assigned to this `&rc` value is not `'a` but something smaller. In
15+
// other words, the type from `rc` is `&'a Rc<String>` and the type
16+
// from `&rc` should be `&'x &'a Rc<String>`, where `'x` is something
17+
// small.
18+
19+
use std::rc::Rc;
20+
21+
#[derive(Clone)]
22+
enum CachedMir<'mir> {
23+
Ref(&'mir String),
24+
Owned(Rc<String>),
25+
}
26+
27+
impl<'mir> CachedMir<'mir> {
28+
fn get_ref<'a>(&'a self) -> &'a String {
29+
match *self {
30+
CachedMir::Ref(r) => r,
31+
CachedMir::Owned(ref rc) => &rc,
32+
}
33+
}
34+
}
35+
36+
fn main() { }

0 commit comments

Comments
 (0)