]>
git.proxmox.com Git - rustc.git/blob - src/librustc/middle/effect.rs
bb63ec42d8c0cb9c87473545e46ea77e8f5b6cdb
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::UnsafeContext
::*;
16 use middle
::ty
::{self, Ty}
;
17 use middle
::ty
::MethodCall
;
20 use syntax
::codemap
::Span
;
22 use syntax
::visit
::Visitor
;
24 #[derive(Copy, Clone, PartialEq)]
28 UnsafeBlock(ast
::NodeId
),
31 fn type_is_unsafe_function(ty
: Ty
) -> bool
{
33 ty
::TyBareFn(_
, ref f
) => f
.unsafety
== ast
::Unsafety
::Unsafe
,
38 struct EffectCheckVisitor
<'a
, 'tcx
: 'a
> {
39 tcx
: &'a ty
::ctxt
<'tcx
>,
41 /// Whether we're in an unsafe context.
42 unsafe_context
: UnsafeContext
,
45 impl<'a
, 'tcx
> EffectCheckVisitor
<'a
, 'tcx
> {
46 fn require_unsafe(&mut self, span
: Span
, description
: &str) {
47 match self.unsafe_context
{
50 span_err
!(self.tcx
.sess
, span
, E0133
,
51 "{} requires unsafe function or block",
54 UnsafeBlock(block_id
) => {
55 // OK, but record this.
56 debug
!("effect: recording unsafe block as used: {}", block_id
);
57 self.tcx
.used_unsafe
.borrow_mut().insert(block_id
);
63 fn check_str_index(&mut self, e
: &ast
::Expr
) {
64 let base_type
= match e
.node
{
65 ast
::ExprIndex(ref base
, _
) => ty
::node_id_to_type(self.tcx
, base
.id
),
68 debug
!("effect: checking index with base type {:?}",
71 ty
::TyBox(ty
) | ty
::TyRef(_
, ty
::mt{ty, ..}
) => if ty
::TyStr
== ty
.sty
{
72 span_err
!(self.tcx
.sess
, e
.span
, E0134
,
73 "modification of string types is not allowed");
76 span_err
!(self.tcx
.sess
, e
.span
, E0135
,
77 "modification of string types is not allowed");
84 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for EffectCheckVisitor
<'a
, 'tcx
> {
85 fn visit_fn(&mut self, fn_kind
: visit
::FnKind
<'v
>, fn_decl
: &'v ast
::FnDecl
,
86 block
: &'v ast
::Block
, span
: Span
, _
: ast
::NodeId
) {
88 let (is_item_fn
, is_unsafe_fn
) = match fn_kind
{
89 visit
::FkItemFn(_
, _
, unsafety
, _
, _
, _
) =>
90 (true, unsafety
== ast
::Unsafety
::Unsafe
),
91 visit
::FkMethod(_
, sig
, _
) =>
92 (true, sig
.unsafety
== ast
::Unsafety
::Unsafe
),
96 let old_unsafe_context
= self.unsafe_context
;
98 self.unsafe_context
= UnsafeFn
99 } else if is_item_fn
{
100 self.unsafe_context
= SafeContext
103 visit
::walk_fn(self, fn_kind
, fn_decl
, block
, span
);
105 self.unsafe_context
= old_unsafe_context
108 fn visit_block(&mut self, block
: &ast
::Block
) {
109 let old_unsafe_context
= self.unsafe_context
;
111 ast
::DefaultBlock
=> {}
112 ast
::UnsafeBlock(source
) => {
113 // By default only the outermost `unsafe` block is
114 // "used" and so nested unsafe blocks are pointless
115 // (the inner ones are unnecessary and we actually
116 // warn about them). As such, there are two cases when
117 // we need to create a new context, when we're
118 // - outside `unsafe` and found a `unsafe` block
120 // - inside `unsafe`, found an `unsafe` block
121 // created internally to the compiler
123 // The second case is necessary to ensure that the
124 // compiler `unsafe` blocks don't accidentally "use"
125 // external blocks (e.g. `unsafe { println("") }`,
126 // expands to `unsafe { ... unsafe { ... } }` where
127 // the inner one is compiler generated).
128 if self.unsafe_context
== SafeContext
|| source
== ast
::CompilerGenerated
{
129 self.unsafe_context
= UnsafeBlock(block
.id
)
134 visit
::walk_block(self, block
);
136 self.unsafe_context
= old_unsafe_context
139 fn visit_expr(&mut self, expr
: &ast
::Expr
) {
141 ast
::ExprMethodCall(_
, _
, _
) => {
142 let method_call
= MethodCall
::expr(expr
.id
);
143 let base_type
= self.tcx
.method_map
.borrow().get(&method_call
).unwrap().ty
;
144 debug
!("effect: method call case, base type is {:?}",
146 if type_is_unsafe_function(base_type
) {
147 self.require_unsafe(expr
.span
,
148 "invocation of unsafe method")
151 ast
::ExprCall(ref base
, _
) => {
152 let base_type
= ty
::node_id_to_type(self.tcx
, base
.id
);
153 debug
!("effect: call case, base type is {:?}",
155 if type_is_unsafe_function(base_type
) {
156 self.require_unsafe(expr
.span
, "call to unsafe function")
159 ast
::ExprUnary(ast
::UnDeref
, ref base
) => {
160 let base_type
= ty
::node_id_to_type(self.tcx
, base
.id
);
161 debug
!("effect: unary case, base type is {:?}",
163 if let ty
::TyRawPtr(_
) = base_type
.sty
{
164 self.require_unsafe(expr
.span
, "dereference of raw pointer")
167 ast
::ExprAssign(ref base
, _
) | ast
::ExprAssignOp(_
, ref base
, _
) => {
168 self.check_str_index(&**base
);
170 ast
::ExprAddrOf(ast
::MutMutable
, ref base
) => {
171 self.check_str_index(&**base
);
173 ast
::ExprInlineAsm(..) => {
174 self.require_unsafe(expr
.span
, "use of inline assembly");
176 ast
::ExprPath(..) => {
177 if let def
::DefStatic(_
, true) = ty
::resolve_expr(self.tcx
, expr
) {
178 self.require_unsafe(expr
.span
, "use of mutable static");
184 visit
::walk_expr(self, expr
);
188 pub fn check_crate(tcx
: &ty
::ctxt
) {
189 let mut visitor
= EffectCheckVisitor
{
191 unsafe_context
: SafeContext
,
194 visit
::walk_crate(&mut visitor
, tcx
.map
.krate());