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,
42 cmt_original: cmt.clone(),
43 root_scope_id: root_scope_id};
44 ctxt.check(&cmt, None)
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> {
69 fn check(&self, cmt: &mc::cmt, discr_scope: Option<ast::NodeId>) -> R {
70 //! Main routine. Walks down `cmt` until we find the "guarantor".
71 debug!("guarantee_lifetime.check(cmt={}, loan_region={})",
72 cmt.repr(self.bccx.tcx),
73 self.loan_region.repr(self.bccx.tcx));
77 mc::cat_copied_upvar(..) | // L-Local
78 mc::cat_local(..) | // L-Local
79 mc::cat_arg(..) | // L-Local
81 mc::cat_deref(_, _, mc::BorrowedPtr(..)) | // L-Deref-Borrowed
82 mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
83 self.check_scope(self.scope(cmt))
86 mc::cat_static_item => {
90 mc::cat_downcast(ref base) |
91 mc::cat_deref(ref base, _, mc::OwnedPtr) | // L-Deref-Send
92 mc::cat_interior(ref base, _) | // L-Field
93 mc::cat_deref(ref base, _, mc::GcPtr) => {
94 self.check(base, discr_scope)
97 mc::cat_discr(ref base, new_discr_scope) => {
98 // Subtle: in a match, we must ensure that each binding
99 // variable remains valid for the duration of the arm in
100 // which it appears, presuming that this arm is taken.
101 // But it is inconvenient in trans to root something just
102 // for one arm. Therefore, we insert a cat_discr(),
103 // basically a special kind of category that says "if this
104 // value must be dynamically rooted, root it for the scope
107 // As an example, consider this scenario:
109 // let mut x = @Some(3);
110 // match *x { Some(y) {...} None {...} }
112 // Technically, the value `x` need only be rooted
113 // in the `some` arm. However, we evaluate `x` in trans
114 // before we know what arm will be taken, so we just
115 // always root it for the duration of the match.
117 // As a second example, consider *this* scenario:
119 // let x = @@Some(3);
120 // match x { @@Some(y) {...} @@None {...} }
122 // Here again, `x` need only be rooted in the `some` arm.
123 // In this case, the value which needs to be rooted is
124 // found only when checking which pattern matches: but
125 // this check is done before entering the arm. Therefore,
126 // even in this case we just choose to keep the value
127 // rooted for the entire match. This means the value will be
128 // rooted even if the none arm is taken. Oh well.
130 // At first, I tried to optimize the second case to only
131 // root in one arm, but the result was suboptimal: first,
132 // it interfered with the construction of phi nodes in the
133 // arm, as we were adding code to root values before the
134 // phi nodes were added. This could have been addressed
135 // with a second basic block. However, the naive approach
136 // also yielded suboptimal results for patterns like:
139 // match x { @@some_variant(y) | @@some_other_variant(y) =>
141 // The reason is that we would root the value once for
142 // each pattern and not once per arm. This is also easily
143 // fixed, but it's yet more code for what is really quite
146 // Nonetheless, if you decide to optimize this case in the
147 // future, you need only adjust where the cat_discr()
148 // node appears to draw the line between what will be rooted
149 // in the *arm* vs the *match*.
150 self.check(base, Some(new_discr_scope))
155 fn check_scope(&self, max_scope: ty::Region) -> R {
156 //! Reports an error if `loan_region` is larger than `valid_scope`
158 if !self.bccx.is_subregion_of(self.loan_region, max_scope) {
159 Err(self.report_error(err_out_of_scope(max_scope, self.loan_region)))
165 fn scope(&self, cmt: &mc::cmt) -> ty::Region {
166 //! Returns the maximal region scope for the which the
167 //! lvalue `cmt` is guaranteed to be valid without any
168 //! rooting etc, and presuming `cmt` is not mutated.
170 // See the SCOPE(LV) function in doc.rs
173 mc::cat_rvalue(temp_scope) => {
177 mc::cat_copied_upvar(_) => {
178 ty::ReScope(self.item_scope_id)
180 mc::cat_static_item => {
183 mc::cat_local(local_id) |
184 mc::cat_arg(local_id) => {
185 ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id))
187 mc::cat_deref(_, _, mc::UnsafePtr(..)) => {
190 mc::cat_deref(_, _, mc::BorrowedPtr(_, r)) => {
193 mc::cat_downcast(ref cmt) |
194 mc::cat_deref(ref cmt, _, mc::OwnedPtr) |
195 mc::cat_deref(ref cmt, _, mc::GcPtr) |
196 mc::cat_interior(ref cmt, _) |
197 mc::cat_discr(ref cmt, _) => {
203 fn report_error(&self, code: bckerr_code) {
204 self.bccx.report(BckError { cmt: self.cmt_original.clone(),