1 // Copyright 2012-2013 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.
11 //! Enforces the Rust effect system. Currently there is just one effect,
13 use self::RootUnsafeContext
::*;
16 use middle
::ty
::{self, Ty}
;
17 use middle
::ty
::MethodCall
;
20 use syntax
::codemap
::Span
;
22 use rustc_front
::visit
;
23 use rustc_front
::visit
::{FnKind, Visitor}
;
25 #[derive(Copy, Clone)]
26 struct UnsafeContext
{
27 push_unsafe_count
: usize,
28 root
: RootUnsafeContext
,
32 fn new(root
: RootUnsafeContext
) -> UnsafeContext
{
33 UnsafeContext { root: root, push_unsafe_count: 0 }
37 #[derive(Copy, Clone, PartialEq)]
38 enum RootUnsafeContext
{
41 UnsafeBlock(ast
::NodeId
),
44 fn type_is_unsafe_function(ty
: Ty
) -> bool
{
46 ty
::TyBareFn(_
, ref f
) => f
.unsafety
== hir
::Unsafety
::Unsafe
,
51 struct EffectCheckVisitor
<'a
, 'tcx
: 'a
> {
52 tcx
: &'a ty
::ctxt
<'tcx
>,
54 /// Whether we're in an unsafe context.
55 unsafe_context
: UnsafeContext
,
58 impl<'a
, 'tcx
> EffectCheckVisitor
<'a
, 'tcx
> {
59 fn require_unsafe(&mut self, span
: Span
, description
: &str) {
60 if self.unsafe_context
.push_unsafe_count
> 0 { return; }
61 match self.unsafe_context
.root
{
64 span_err
!(self.tcx
.sess
, span
, E0133
,
65 "{} requires unsafe function or block",
68 UnsafeBlock(block_id
) => {
69 // OK, but record this.
70 debug
!("effect: recording unsafe block as used: {}", block_id
);
71 self.tcx
.used_unsafe
.borrow_mut().insert(block_id
);
78 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for EffectCheckVisitor
<'a
, 'tcx
> {
79 fn visit_fn(&mut self, fn_kind
: FnKind
<'v
>, fn_decl
: &'v hir
::FnDecl
,
80 block
: &'v hir
::Block
, span
: Span
, _
: ast
::NodeId
) {
82 let (is_item_fn
, is_unsafe_fn
) = match fn_kind
{
83 FnKind
::ItemFn(_
, _
, unsafety
, _
, _
, _
) =>
84 (true, unsafety
== hir
::Unsafety
::Unsafe
),
85 FnKind
::Method(_
, sig
, _
) =>
86 (true, sig
.unsafety
== hir
::Unsafety
::Unsafe
),
90 let old_unsafe_context
= self.unsafe_context
;
92 self.unsafe_context
= UnsafeContext
::new(UnsafeFn
)
93 } else if is_item_fn
{
94 self.unsafe_context
= UnsafeContext
::new(SafeContext
)
97 visit
::walk_fn(self, fn_kind
, fn_decl
, block
, span
);
99 self.unsafe_context
= old_unsafe_context
102 fn visit_block(&mut self, block
: &hir
::Block
) {
103 let old_unsafe_context
= self.unsafe_context
;
105 hir
::DefaultBlock
=> {}
106 hir
::UnsafeBlock(source
) => {
107 // By default only the outermost `unsafe` block is
108 // "used" and so nested unsafe blocks are pointless
109 // (the inner ones are unnecessary and we actually
110 // warn about them). As such, there are two cases when
111 // we need to create a new context, when we're
112 // - outside `unsafe` and found a `unsafe` block
114 // - inside `unsafe`, found an `unsafe` block
115 // created internally to the compiler
117 // The second case is necessary to ensure that the
118 // compiler `unsafe` blocks don't accidentally "use"
119 // external blocks (e.g. `unsafe { println("") }`,
120 // expands to `unsafe { ... unsafe { ... } }` where
121 // the inner one is compiler generated).
122 if self.unsafe_context
.root
== SafeContext
|| source
== hir
::CompilerGenerated
{
123 self.unsafe_context
.root
= UnsafeBlock(block
.id
)
126 hir
::PushUnsafeBlock(..) => {
127 self.unsafe_context
.push_unsafe_count
=
128 self.unsafe_context
.push_unsafe_count
.checked_add(1).unwrap();
130 hir
::PopUnsafeBlock(..) => {
131 self.unsafe_context
.push_unsafe_count
=
132 self.unsafe_context
.push_unsafe_count
.checked_sub(1).unwrap();
136 visit
::walk_block(self, block
);
138 self.unsafe_context
= old_unsafe_context
141 fn visit_expr(&mut self, expr
: &hir
::Expr
) {
143 hir
::ExprMethodCall(_
, _
, _
) => {
144 let method_call
= MethodCall
::expr(expr
.id
);
145 let base_type
= self.tcx
.tables
.borrow().method_map
[&method_call
].ty
;
146 debug
!("effect: method call case, base type is {:?}",
148 if type_is_unsafe_function(base_type
) {
149 self.require_unsafe(expr
.span
,
150 "invocation of unsafe method")
153 hir
::ExprCall(ref base
, _
) => {
154 let base_type
= self.tcx
.node_id_to_type(base
.id
);
155 debug
!("effect: call case, base type is {:?}",
157 if type_is_unsafe_function(base_type
) {
158 self.require_unsafe(expr
.span
, "call to unsafe function")
161 hir
::ExprUnary(hir
::UnDeref
, ref base
) => {
162 let base_type
= self.tcx
.node_id_to_type(base
.id
);
163 debug
!("effect: unary case, base type is {:?}",
165 if let ty
::TyRawPtr(_
) = base_type
.sty
{
166 self.require_unsafe(expr
.span
, "dereference of raw pointer")
169 hir
::ExprInlineAsm(..) => {
170 self.require_unsafe(expr
.span
, "use of inline assembly");
172 hir
::ExprPath(..) => {
173 if let def
::DefStatic(_
, true) = self.tcx
.resolve_expr(expr
) {
174 self.require_unsafe(expr
.span
, "use of mutable static");
180 visit
::walk_expr(self, expr
);
184 pub fn check_crate(tcx
: &ty
::ctxt
) {
185 let mut visitor
= EffectCheckVisitor
{
187 unsafe_context
: UnsafeContext
::new(SafeContext
),
190 visit
::walk_crate(&mut visitor
, tcx
.map
.krate());