Skip to content

Commit 5bc17f2

Browse files
committed
Reason about nested free variables that appear in a function
signature. In a nutshell, the idea is to (1) report an error if, for a region pointer `'a T`, the lifetime `'a` is longer than any lifetimes that appear in `T` (in other words, if a borrowed pointer outlives any portion of its contents) and then (2) use this to assume that in a function like `fn(self: &'a &'b T)`, the relationship `'a <= 'b` holds. This is needed for rust-lang#5656. Fixes rust-lang#5728.
1 parent 44d4d6d commit 5bc17f2

27 files changed

+1030
-350
lines changed

src/libcore/cmp.rs

+21
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,19 @@ totalord_impl!(i64)
9898
totalord_impl!(int)
9999
totalord_impl!(uint)
100100

101+
pub fn cmp2<A:TotalOrd,B:TotalOrd>(
102+
a1: &A, b1: &B,
103+
a2: &A, b2: &B) -> Ordering
104+
{
105+
//! Compares (a1, b1) against (a2, b2), where the a values are more significant.
106+
107+
match a1.cmp(a2) {
108+
Less => Less,
109+
Greater => Greater,
110+
Equal => b1.cmp(b2)
111+
}
112+
}
113+
101114
/**
102115
* Trait for values that can be compared for a sort-order.
103116
*
@@ -175,6 +188,14 @@ mod test {
175188
assert_eq!(12.cmp(-5), Greater);
176189
}
177190

191+
#[test]
192+
fn test_cmp2() {
193+
assert_eq!(cmp2(1, 2, 3, 4), Less);
194+
assert_eq!(cmp2(3, 2, 3, 4), Less);
195+
assert_eq!(cmp2(5, 2, 3, 4), Greater);
196+
assert_eq!(cmp2(5, 5, 5, 4), Greater);
197+
}
198+
178199
#[test]
179200
fn test_int_totaleq() {
180201
assert!(5.equals(&5));

src/libcore/str.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,11 @@ pub fn slice_shift_char<'a>(s: &'a str) -> (char, &'a str) {
301301

302302
/// Prepend a char to a string
303303
pub fn unshift_char(s: &mut ~str, ch: char) {
304-
*s = from_char(ch) + *s;
304+
// This could be more efficient.
305+
let mut new_str = ~"";
306+
new_str.push_char(ch);
307+
new_str.push_str(*s);
308+
*s = new_str;
305309
}
306310

307311
/**

src/libcore/tuple.rs

-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ impl<A:Ord> Ord for (A,) {
161161
fn gt(&self, other: &(A,)) -> bool { other.lt(&(*self)) }
162162
}
163163

164-
165164
#[cfg(notest)]
166165
impl<A:Eq,B:Eq> Eq for (A, B) {
167166
#[inline(always)]

src/librustc/metadata/tydecode.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ fn parse_region(st: @mut PState) -> ty::Region {
239239
assert!(next(st) == '|');
240240
let br = parse_bound_region(st);
241241
assert!(next(st) == ']');
242-
ty::re_free(id, br)
242+
ty::re_free(ty::FreeRegion {scope_id: id,
243+
bound_region: br})
243244
}
244245
's' => {
245246
let id = parse_int(st);

src/librustc/metadata/tyencode.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -146,12 +146,12 @@ fn enc_region(w: @io::Writer, cx: @ctxt, r: ty::Region) {
146146
w.write_char('b');
147147
enc_bound_region(w, cx, br);
148148
}
149-
ty::re_free(id, br) => {
149+
ty::re_free(ref fr) => {
150150
w.write_char('f');
151151
w.write_char('[');
152-
w.write_int(id);
152+
w.write_int(fr.scope_id);
153153
w.write_char('|');
154-
enc_bound_region(w, cx, br);
154+
enc_bound_region(w, cx, fr.bound_region);
155155
w.write_char(']');
156156
}
157157
ty::re_scope(nid) => {

src/librustc/middle/astencode.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,12 @@ impl tr for ty::Region {
475475
fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::Region {
476476
match *self {
477477
ty::re_bound(br) => ty::re_bound(br.tr(xcx)),
478-
ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)),
479478
ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)),
480479
ty::re_static | ty::re_infer(*) => *self,
480+
ty::re_free(ref fr) => {
481+
ty::re_free(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id),
482+
bound_region: fr.bound_region.tr(xcx)})
483+
}
481484
}
482485
}
483486
}

src/librustc/middle/borrowck/check_loans.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ pub impl CheckLoanCtxt {
128128
Some(e) => return Some(pc_cmt(*e))
129129
}
130130
131-
match self.tcx().region_map.find(&scope_id) {
131+
match self.tcx().region_maps.opt_encl_scope(scope_id) {
132132
None => return default_purity,
133-
Some(&next_scope_id) => scope_id = next_scope_id
133+
Some(next_scope_id) => scope_id = next_scope_id
134134
}
135135
}
136136
}
@@ -146,9 +146,9 @@ pub impl CheckLoanCtxt {
146146
}
147147
}
148148
149-
match self.tcx().region_map.find(&scope_id) {
149+
match self.tcx().region_maps.opt_encl_scope(scope_id) {
150150
None => return,
151-
Some(&next_scope_id) => scope_id = next_scope_id,
151+
Some(next_scope_id) => scope_id = next_scope_id,
152152
}
153153
}
154154
}
@@ -270,7 +270,7 @@ pub impl CheckLoanCtxt {
270270

271271
debug!("new_loans has length %?", new_loans.len());
272272

273-
let par_scope_id = *self.tcx().region_map.get(&scope_id);
273+
let par_scope_id = self.tcx().region_maps.encl_scope(scope_id);
274274
for self.walk_loans(par_scope_id) |old_loan| {
275275
debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan));
276276

src/librustc/middle/borrowck/gather_loans.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ fn req_loans_in_expr(ex: @ast::expr,
242242
// (if used like `a.b(...)`), the call where it's an argument
243243
// (if used like `x(a.b)`), or the block (if used like `let x
244244
// = a.b`).
245-
let scope_r = ty::re_scope(*self.tcx().region_map.get(&ex.id));
245+
let scope_r = self.tcx().region_maps.encl_region(ex.id);
246246
let rcvr_cmt = self.bccx.cat_expr(rcvr);
247247
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
248248
visit::visit_expr(ex, self, vt);
@@ -524,7 +524,10 @@ pub impl GatherLoanCtxt {
524524
// immutable structures, this is just the converse I suppose)
525525

526526
let scope_id = match scope_r {
527-
ty::re_scope(scope_id) | ty::re_free(scope_id, _) => scope_id,
527+
ty::re_scope(scope_id) |
528+
ty::re_free(ty::FreeRegion {scope_id, _}) => {
529+
scope_id
530+
}
528531
_ => {
529532
self.bccx.tcx.sess.span_bug(
530533
cmt.span,

src/librustc/middle/borrowck/loan.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ pub impl LoanContext {
130130
}
131131
cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => {
132132
// FIXME(#4903)
133-
let local_scope_id = *self.bccx.tcx.region_map.get(&local_id);
134-
self.issue_loan(cmt, ty::re_scope(local_scope_id), loan_kind,
133+
let local_region = self.bccx.tcx.region_maps.encl_region(local_id);
134+
self.issue_loan(cmt, local_region, loan_kind,
135135
owns_lent_data)
136136
}
137137
cat_stack_upvar(cmt) => {

src/librustc/middle/borrowck/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ Borrowck results in two maps.
227227
use core::prelude::*;
228228

229229
use middle::mem_categorization::*;
230-
use middle::region;
231230
use middle::ty;
232231
use middle::typeck;
233232
use middle::moves;
@@ -458,7 +457,7 @@ pub fn root_map() -> root_map {
458457

459458
pub impl BorrowckCtxt {
460459
fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region) -> bool {
461-
region::is_subregion_of(self.tcx.region_map, r_sub, r_sup)
460+
self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
462461
}
463462

464463
fn cat_expr(&self, expr: @ast::expr) -> cmt {

src/librustc/middle/borrowck/preserve.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ pub impl<'self> PreserveCtxt<'self> {
108108
// Maybe if we pass in the parent instead here,
109109
// we can prevent the "scope not found" error
110110
debug!("scope_region thing: %? ", cmt.id);
111-
ty::re_scope(*self.tcx().region_map.get(&cmt.id))
111+
self.tcx().region_maps.encl_region(cmt.id)
112112
};
113113

114114
self.compare_scope(cmt, scope_region)
@@ -128,27 +128,27 @@ pub impl<'self> PreserveCtxt<'self> {
128128
cmt.span,
129129
~"preserve() called with local and !root_managed_data");
130130
}
131-
let local_scope_id = *self.tcx().region_map.get(&local_id);
132-
self.compare_scope(cmt, ty::re_scope(local_scope_id))
131+
let local_region = self.tcx().region_maps.encl_region(local_id);
132+
self.compare_scope(cmt, local_region)
133133
}
134134
cat_binding(local_id) => {
135135
// Bindings are these kind of weird implicit pointers (cc
136136
// #2329). We require (in gather_loans) that they be
137137
// rooted in an immutable location.
138-
let local_scope_id = *self.tcx().region_map.get(&local_id);
139-
self.compare_scope(cmt, ty::re_scope(local_scope_id))
138+
let local_region = self.tcx().region_maps.encl_region(local_id);
139+
self.compare_scope(cmt, local_region)
140140
}
141141
cat_arg(local_id) => {
142142
// This can happen as not all args are lendable (e.g., &&
143143
// modes). In that case, the caller guarantees stability
144144
// for at least the scope of the fn. This is basically a
145145
// deref of a region ptr.
146-
let local_scope_id = *self.tcx().region_map.get(&local_id);
147-
self.compare_scope(cmt, ty::re_scope(local_scope_id))
146+
let local_region = self.tcx().region_maps.encl_region(local_id);
147+
self.compare_scope(cmt, local_region)
148148
}
149149
cat_self(local_id) => {
150-
let local_scope_id = *self.tcx().region_map.get(&local_id);
151-
self.compare_scope(cmt, ty::re_scope(local_scope_id))
150+
let local_region = self.tcx().region_maps.encl_region(local_id);
151+
self.compare_scope(cmt, local_region)
152152
}
153153
cat_comp(cmt_base, comp_field(*)) |
154154
cat_comp(cmt_base, comp_index(*)) |

src/librustc/middle/check_match.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,11 @@ pub fn specialize(cx: @MatchCheckCtxt,
596596
class_id);
597597
}
598598
_ => {
599-
cx.tcx.sess.span_bug(pat_span,
600-
~"struct pattern didn't resolve to a struct");
599+
cx.tcx.sess.span_bug(
600+
pat_span,
601+
fmt!("struct pattern resolved to %s, \
602+
not a struct",
603+
ty_to_str(cx.tcx, left_ty)));
601604
}
602605
}
603606
let args = vec::map(class_fields, |class_field| {

src/librustc/middle/kind.rs

+71-31
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use middle::liveness;
1616
use middle::pat_util;
1717
use middle::ty;
1818
use middle::typeck;
19-
use util::ppaux::{ty_to_str, tys_to_str};
19+
use util::ppaux::{ty_to_str, tys_to_str, note_and_explain_region};
2020

2121
use syntax::ast::*;
2222
use syntax::attr::attrs_contains_name;
@@ -477,13 +477,13 @@ pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool {
477477
}
478478
}
479479

480-
/// This is rather subtle. When we are casting a value to a
481-
/// instantiated trait like `a as trait<'r>`, regionck already ensures
482-
/// that any borrowed pointers that appear in the type of `a` are
483-
/// bounded by `&r`. However, it is possible that there are *type
484-
/// parameters* in the type of `a`, and those *type parameters* may
485-
/// have borrowed pointers within them. We have to guarantee that the
486-
/// regions which appear in those type parameters are not obscured.
480+
/// This is rather subtle. When we are casting a value to a instantiated
481+
/// trait like `a as trait<'r>`, regionck already ensures that any borrowed
482+
/// pointers that appear in the type of `a` are bounded by `'r` (ed.: modulo
483+
/// FIXME(#5723)). However, it is possible that there are *type parameters*
484+
/// in the type of `a`, and those *type parameters* may have borrowed pointers
485+
/// within them. We have to guarantee that the regions which appear in those
486+
/// type parameters are not obscured.
487487
///
488488
/// Therefore, we ensure that one of three conditions holds:
489489
///
@@ -500,6 +500,8 @@ pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool {
500500
///
501501
/// (3) The type parameter is owned (and therefore does not contain
502502
/// borrowed ptrs).
503+
///
504+
/// FIXME(#5723)---This code should probably move into regionck.
503505
pub fn check_cast_for_escaping_regions(
504506
cx: Context,
505507
source: @expr,
@@ -508,40 +510,78 @@ pub fn check_cast_for_escaping_regions(
508510
// Determine what type we are casting to; if it is not an trait, then no
509511
// worries.
510512
let target_ty = ty::expr_ty(cx.tcx, target);
511-
let target_substs = match ty::get(target_ty).sty {
512-
ty::ty_trait(_, ref substs, _) => {(/*bad*/copy *substs)}
513-
_ => { return; /* not a cast to a trait */ }
514-
};
513+
match ty::get(target_ty).sty {
514+
ty::ty_trait(*) => {}
515+
_ => { return; }
516+
}
517+
518+
// Collect up the regions that appear in the target type. We want to
519+
// ensure that these lifetimes are shorter than all lifetimes that are in
520+
// the source type. See test `src/test/compile-fail/regions-trait-2.rs`
521+
let mut target_regions = ~[];
522+
ty::walk_regions_and_ty(
523+
cx.tcx,
524+
target_ty,
525+
|r| {
526+
if !r.is_bound() {
527+
target_regions.push(r);
528+
}
529+
},
530+
|_| true);
515531

516532
// Check, based on the region associated with the trait, whether it can
517533
// possibly escape the enclosing fn item (note that all type parameters
518-
// must have been declared on the enclosing fn item):
519-
match target_substs.self_r {
520-
Some(ty::re_scope(*)) => { return; /* case (1) */ }
521-
None | Some(ty::re_static) | Some(ty::re_free(*)) => {}
522-
Some(ty::re_bound(*)) | Some(ty::re_infer(*)) => {
523-
cx.tcx.sess.span_bug(
524-
source.span,
525-
fmt!("bad region found in kind: %?", target_substs.self_r));
526-
}
534+
// must have been declared on the enclosing fn item).
535+
if target_regions.any(|r| is_re_scope(*r)) {
536+
return; /* case (1) */
527537
}
528538

529539
// Assuming the trait instance can escape, then ensure that each parameter
530-
// either appears in the trait type or is owned:
540+
// either appears in the trait type or is owned.
531541
let target_params = ty::param_tys_in_type(target_ty);
532542
let source_ty = ty::expr_ty(cx.tcx, source);
533-
do ty::walk_ty(source_ty) |ty| {
534-
match ty::get(ty).sty {
535-
ty::ty_param(source_param) => {
536-
if target_params.contains(&source_param) {
537-
/* case (2) */
538-
} else {
539-
check_durable(cx.tcx, ty, source.span); /* case (3) */
543+
ty::walk_regions_and_ty(
544+
cx.tcx,
545+
source_ty,
546+
547+
|_r| {
548+
// FIXME(#5723) --- turn this check on once &Objects are usable
549+
//
550+
// if !target_regions.any(|t_r| is_subregion_of(cx, *t_r, r)) {
551+
// cx.tcx.sess.span_err(
552+
// source.span,
553+
// fmt!("source contains borrowed pointer with lifetime \
554+
// not found in the target type `%s`",
555+
// ty_to_str(cx.tcx, target_ty)));
556+
// note_and_explain_region(
557+
// cx.tcx, "source data is only valid for ", r, "");
558+
// }
559+
},
560+
561+
|ty| {
562+
match ty::get(ty).sty {
563+
ty::ty_param(source_param) => {
564+
if target_params.contains(&source_param) {
565+
/* case (2) */
566+
} else {
567+
check_durable(cx.tcx, ty, source.span); /* case (3) */
568+
}
569+
}
570+
_ => {}
540571
}
541-
}
542-
_ => {}
572+
true
573+
});
574+
575+
fn is_re_scope(+r: ty::Region) -> bool {
576+
match r {
577+
ty::re_scope(*) => true,
578+
_ => false
543579
}
544580
}
581+
582+
fn is_subregion_of(cx: Context, r_sub: ty::Region, r_sup: ty::Region) -> bool {
583+
cx.tcx.region_maps.is_subregion_of(r_sub, r_sup)
584+
}
545585
}
546586

547587
/// Ensures that values placed into a ~Trait are copyable and sendable.

0 commit comments

Comments
 (0)