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.
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.
12 * This module implements the check that the lifetime of a borrow
13 * does not exceed the lifetime of the value being borrowed.
16 use middle::borrowck::*;
17 use mc = middle::mem_categorization;
19 use util::ppaux::Repr;
21 use syntax::codemap::Span;
23 type R = Result<(),()>;
25 pub fn guarantee_lifetime(bccx: &BorrowckCtxt,
26 item_scope_id: ast::NodeId,
27 root_scope_id: ast::NodeId,
31 loan_region: ty::Region,
32 loan_kind: ty::BorrowKind)
34 debug!("guarantee_lifetime(cmt={}, loan_region={})",
35 cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx));
36 let ctxt = GuaranteeLifetimeContext {bccx: bccx,
37 item_scope_id: item_scope_id,
40 loan_region: loan_region,
43 root_scope_id: root_scope_id};
47 ///////////////////////////////////////////////////////////////////////////
50 struct GuaranteeLifetimeContext<'a> {
51 bccx: &'a BorrowckCtxt<'a>,
53 // the node id of the function body for the enclosing item
54 item_scope_id: ast::NodeId,
56 // the node id of the innermost loop / function body; this is the
57 // longest scope for which we can root managed boxes
58 root_scope_id: ast::NodeId,
62 loan_region: ty::Region,
63 loan_kind: ty::BorrowKind,
67 impl<'a> GuaranteeLifetimeContext<'a> {
68 fn tcx(&self) -> &'a ty::ctxt {
72 fn check(&self, cmt: mc::cmt, discr_scope: Option<ast::NodeId>) -> R {
73 //! Main routine. Walks down `cmt` until we find the "guarantor".
74 debug!("guarantee_lifetime.check(cmt={}, loan_region={})",
75 cmt.repr(self.bccx.tcx),
76 self.loan_region.repr(self.bccx.tcx));
80 mc::cat_copied_upvar(..) | // L-Local
81 mc::cat_local(..) | // L-Local
82 mc::cat_arg(..) | // L-Local
84 mc::cat_deref(_, _, mc::BorrowedPtr(..)) | // L-Deref-Borrowed
85 mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
86 let scope = self.scope(cmt);
87 self.check_scope(scope)
90 mc::cat_static_item => {
94 mc::cat_deref(base, derefs, mc::GcPtr) => {
95 let base_scope = self.scope(base);
97 // L-Deref-Managed-Imm-User-Root
99 self.bccx.is_subregion_of(self.loan_region, base_scope) &&
100 self.is_rvalue_or_immutable(base) &&
101 !self.is_moved(base);
104 // L-Deref-Managed-Imm-Compiler-Root
105 // L-Deref-Managed-Mut-Compiler-Root
106 self.check_root(cmt, base, derefs, discr_scope)
108 debug!("omitting root, base={}, base_scope={:?}",
109 base.repr(self.tcx()), base_scope);
114 mc::cat_downcast(base) |
115 mc::cat_deref(base, _, mc::OwnedPtr) | // L-Deref-Send
116 mc::cat_interior(base, _) => { // L-Field
117 self.check(base, discr_scope)
120 mc::cat_discr(base, new_discr_scope) => {
121 // Subtle: in a match, we must ensure that each binding
122 // variable remains valid for the duration of the arm in
123 // which it appears, presuming that this arm is taken.
124 // But it is inconvenient in trans to root something just
125 // for one arm. Therefore, we insert a cat_discr(),
126 // basically a special kind of category that says "if this
127 // value must be dynamically rooted, root it for the scope
130 // As an example, consider this scenario:
132 // let mut x = @Some(3);
133 // match *x { Some(y) {...} None {...} }
135 // Technically, the value `x` need only be rooted
136 // in the `some` arm. However, we evaluate `x` in trans
137 // before we know what arm will be taken, so we just
138 // always root it for the duration of the match.
140 // As a second example, consider *this* scenario:
142 // let x = @@Some(3);
143 // match x { @@Some(y) {...} @@None {...} }
145 // Here again, `x` need only be rooted in the `some` arm.
146 // In this case, the value which needs to be rooted is
147 // found only when checking which pattern matches: but
148 // this check is done before entering the arm. Therefore,
149 // even in this case we just choose to keep the value
150 // rooted for the entire match. This means the value will be
151 // rooted even if the none arm is taken. Oh well.
153 // At first, I tried to optimize the second case to only
154 // root in one arm, but the result was suboptimal: first,
155 // it interfered with the construction of phi nodes in the
156 // arm, as we were adding code to root values before the
157 // phi nodes were added. This could have been addressed
158 // with a second basic block. However, the naive approach
159 // also yielded suboptimal results for patterns like:
162 // match x { @@some_variant(y) | @@some_other_variant(y) =>
164 // The reason is that we would root the value once for
165 // each pattern and not once per arm. This is also easily
166 // fixed, but it's yet more code for what is really quite
169 // Nonetheless, if you decide to optimize this case in the
170 // future, you need only adjust where the cat_discr()
171 // node appears to draw the line between what will be rooted
172 // in the *arm* vs the *match*.
173 self.check(base, Some(new_discr_scope))
178 fn is_rvalue_or_immutable(&self,
179 cmt: mc::cmt) -> bool {
180 //! We can omit the root on an `@T` value if the location
181 //! that holds the box is either (1) an rvalue, in which case
182 //! it is in a non-user-accessible temporary, or (2) an immutable
185 cmt.mutbl.is_immutable() || match cmt.guarantor().cat {
186 mc::cat_rvalue(..) => true,
195 discr_scope: Option<ast::NodeId>) -> R {
196 debug!("check_root(cmt_deref={}, cmt_base={}, derefs={:?}, \
198 cmt_deref.repr(self.tcx()),
199 cmt_base.repr(self.tcx()),
203 // Make sure that the loan does not exceed the maximum time
204 // that we can root the value, dynamically.
205 let root_region = ty::ReScope(self.root_scope_id);
206 if !self.bccx.is_subregion_of(self.loan_region, root_region) {
207 return Err(self.report_error(
208 err_out_of_root_scope(root_region, self.loan_region)));
211 // Extract the scope id that indicates how long the rooting is required
212 let root_scope = match self.loan_region {
213 ty::ReScope(id) => id,
215 // the check above should fail for anything is not ReScope
216 self.bccx.tcx.sess.span_bug(
218 format!("cannot issue root for scope region: {:?}",
223 // If inside of a match arm, expand the rooting to the entire
224 // match. See the detailed discussion in `check()` above.
225 let root_scope = match discr_scope {
228 if self.bccx.is_subscope_of(root_scope, id) {
236 // Add a record of what is required
237 let rm_key = root_map_key {id: cmt_deref.id, derefs: derefs};
238 let root_info = RootInfo {scope: root_scope};
240 self.bccx.root_map.borrow_mut().insert(rm_key, root_info);
242 debug!("root_key: {:?} root_info: {:?}", rm_key, root_info);
246 fn check_scope(&self, max_scope: ty::Region) -> R {
247 //! Reports an error if `loan_region` is larger than `valid_scope`
249 if !self.bccx.is_subregion_of(self.loan_region, max_scope) {
250 Err(self.report_error(err_out_of_scope(max_scope, self.loan_region)))
256 fn is_moved(&self, cmt: mc::cmt) -> bool {
257 //! True if `cmt` is something that is potentially moved
258 //! out of the current stack frame.
260 match cmt.guarantor().cat {
263 self.bccx.moved_variables_set.contains(&id)
266 mc::cat_static_item |
267 mc::cat_copied_upvar(..) |
269 mc::cat_upvar(..) => {
272 r @ mc::cat_downcast(..) |
273 r @ mc::cat_interior(..) |
274 r @ mc::cat_discr(..) => {
275 self.tcx().sess.span_bug(
277 format!("illegal guarantor category: {:?}", r));
282 fn scope(&self, cmt: mc::cmt) -> ty::Region {
283 //! Returns the maximal region scope for the which the
284 //! lvalue `cmt` is guaranteed to be valid without any
285 //! rooting etc, and presuming `cmt` is not mutated.
287 // See the SCOPE(LV) function in doc.rs
290 mc::cat_rvalue(temp_scope) => {
294 mc::cat_copied_upvar(_) => {
295 ty::ReScope(self.item_scope_id)
297 mc::cat_static_item => {
300 mc::cat_local(local_id) |
301 mc::cat_arg(local_id) => {
302 ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id))
304 mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
307 mc::cat_deref(_, _, mc::BorrowedPtr(_, r)) => {
310 mc::cat_downcast(cmt) |
311 mc::cat_deref(cmt, _, mc::OwnedPtr) |
312 mc::cat_deref(cmt, _, mc::GcPtr) |
313 mc::cat_interior(cmt, _) |
314 mc::cat_discr(cmt, _) => {
320 fn report_error(&self, code: bckerr_code) {
321 self.bccx.report(BckError { cmt: self.cmt_original,