summary refs log tree commit diff
path: root/src/rustc/middle/regionck.rs
blob: a969785cd2e1c0e42b68fe50336a4555ea2bb4e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
 * The region checking pass. Ensures that region-annotated pointers never
 * outlive their referents.
 */

import driver::session::session;
import middle::ty;
import std::map::hashmap;
import syntax::{ast, visit};

// An "extended region", which includes the ordinarily-unnamed reference-
// counted heap and exchange heap regions. This is used to detect borrowing.
enum region_ext {
    re_rc,
    re_exheap,
    re_region(ty::region)
}

type ctxt = {
    tcx: ty::ctxt,
    enclosing_block: option<ast::node_id>
};

fn check_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
    let t = ty::expr_ty(cx.tcx, expr);
    if ty::type_has_rptrs(t) {
        ty::walk_ty(t) { |t|
            alt ty::get(t).struct {
                ty::ty_rptr(region, _) {
                    alt region {
                        ty::re_self | ty::re_inferred | ty::re_param(_) {
                            /* ok */
                        }
                        ty::re_block(rbi) {
                            let referent_block_id = rbi;
                            let enclosing_block_id = alt cx.enclosing_block {
                                none {
                                    cx.tcx.sess.span_bug(expr.span,
                                                         "block region " +
                                                         "type outside a " +
                                                         "block?!");
                                }
                                some(eb) { eb }
                            };

                            if !region::scope_contains(cx.tcx.region_map,
                                                       referent_block_id,
                                                       enclosing_block_id) {

                                cx.tcx.sess.span_err(expr.span, "reference " +
                                                     "escapes its block");
                            }
                        }
                        ty::re_var(_) {
                            cx.tcx.sess.span_bug(expr.span,
                                                 "unresolved region");
                        }
                    }
                }
                _ { /* no-op */ }
            }
        }
    }

    visit::visit_expr(expr, cx, visitor);
}

fn check_block(blk: ast::blk, cx: ctxt, visitor: visit::vt<ctxt>) {
    let new_cx: ctxt = { enclosing_block: some(blk.node.id) with cx };
    visit::visit_block(blk, new_cx, visitor);
}

fn check_crate(ty_cx: ty::ctxt, crate: @ast::crate) {
    let cx: ctxt = {tcx: ty_cx, enclosing_block: none};
    let visitor = visit::mk_vt(@{
        visit_expr: check_expr,
        visit_block: check_block
        with *visit::default_visitor()
    });
    visit::visit_crate(*crate, cx, visitor);
}