Skip to content

Commit a69e4a5

Browse files
committed
Forbid modifications of strings in the compiler
This disallows `str[0] = foo` along with `foo = &mut str[i]` to prevent strings from being modified at runtime (except possibly through the `str` module) Closes #8891
1 parent 8eb28bb commit a69e4a5

File tree

2 files changed

+118
-96
lines changed

2 files changed

+118
-96
lines changed

src/librustc/middle/effect.rs

Lines changed: 100 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,14 @@
1111
//! Enforces the Rust effect system. Currently there is just one effect,
1212
/// `unsafe`.
1313
14-
use middle::ty::{ty_bare_fn, ty_closure, ty_ptr};
1514
use middle::ty;
1615
use middle::typeck::method_map;
1716
use util::ppaux;
1817

19-
use syntax::ast::{UnDeref, ExprCall, ExprInlineAsm, ExprMethodCall};
20-
use syntax::ast::{ExprUnary, unsafe_fn, ExprPath};
2118
use syntax::ast;
2219
use syntax::codemap::Span;
23-
use syntax::visit::{fk_item_fn, fk_method};
2420
use syntax::visit;
25-
use syntax::visit::{Visitor,fn_kind};
26-
use syntax::ast::{fn_decl,Block,NodeId,Expr};
21+
use syntax::visit::Visitor;
2722

2823
#[deriving(Eq)]
2924
enum UnsafeContext {
@@ -32,29 +27,26 @@ enum UnsafeContext {
3227
UnsafeBlock(ast::NodeId),
3328
}
3429

35-
struct Context {
36-
/// The method map.
37-
method_map: method_map,
38-
/// Whether we're in an unsafe context.
39-
unsafe_context: UnsafeContext,
40-
}
41-
4230
fn type_is_unsafe_function(ty: ty::t) -> bool {
4331
match ty::get(ty).sty {
44-
ty_bare_fn(ref f) => f.purity == unsafe_fn,
45-
ty_closure(ref f) => f.purity == unsafe_fn,
32+
ty::ty_bare_fn(ref f) => f.purity == ast::unsafe_fn,
33+
ty::ty_closure(ref f) => f.purity == ast::unsafe_fn,
4634
_ => false,
4735
}
4836
}
4937

5038
struct EffectCheckVisitor {
5139
tcx: ty::ctxt,
52-
context: @mut Context,
40+
41+
/// The method map.
42+
method_map: method_map,
43+
/// Whether we're in an unsafe context.
44+
unsafe_context: UnsafeContext,
5345
}
5446

5547
impl EffectCheckVisitor {
5648
fn require_unsafe(&mut self, span: Span, description: &str) {
57-
match self.context.unsafe_context {
49+
match self.unsafe_context {
5850
SafeContext => {
5951
// Report an error.
6052
self.tcx.sess.span_err(span,
@@ -69,112 +61,124 @@ impl EffectCheckVisitor {
6961
UnsafeFn => {}
7062
}
7163
}
64+
65+
fn check_str_index(&mut self, e: @ast::Expr) {
66+
let base_type = match e.node {
67+
ast::ExprIndex(_, base, _) => ty::node_id_to_type(self.tcx, base.id),
68+
_ => return
69+
};
70+
debug2!("effect: checking index with base type {}",
71+
ppaux::ty_to_str(self.tcx, base_type));
72+
match ty::get(base_type).sty {
73+
ty::ty_estr(*) => {
74+
self.tcx.sess.span_err(e.span,
75+
"modification of string types is not allowed");
76+
}
77+
_ => {}
78+
}
79+
}
7280
}
7381

7482
impl Visitor<()> for EffectCheckVisitor {
75-
fn visit_fn(&mut self, fn_kind:&fn_kind, fn_decl:&fn_decl,
76-
block:&Block, span:Span, node_id:NodeId, _:()) {
77-
78-
let (is_item_fn, is_unsafe_fn) = match *fn_kind {
79-
fk_item_fn(_, _, purity, _) => (true, purity == unsafe_fn),
80-
fk_method(_, _, method) => (true, method.purity == unsafe_fn),
81-
_ => (false, false),
82-
};
83-
84-
let old_unsafe_context = self.context.unsafe_context;
85-
if is_unsafe_fn {
86-
self.context.unsafe_context = UnsafeFn
87-
} else if is_item_fn {
88-
self.context.unsafe_context = SafeContext
89-
}
83+
fn visit_fn(&mut self, fn_kind: &visit::fn_kind, fn_decl: &ast::fn_decl,
84+
block: &ast::Block, span: Span, node_id: ast::NodeId, _:()) {
85+
86+
let (is_item_fn, is_unsafe_fn) = match *fn_kind {
87+
visit::fk_item_fn(_, _, purity, _) =>
88+
(true, purity == ast::unsafe_fn),
89+
visit::fk_method(_, _, method) =>
90+
(true, method.purity == ast::unsafe_fn),
91+
_ => (false, false),
92+
};
93+
94+
let old_unsafe_context = self.unsafe_context;
95+
if is_unsafe_fn {
96+
self.unsafe_context = UnsafeFn
97+
} else if is_item_fn {
98+
self.unsafe_context = SafeContext
99+
}
90100

91-
visit::walk_fn(self,
92-
fn_kind,
93-
fn_decl,
94-
block,
95-
span,
96-
node_id,
97-
());
101+
visit::walk_fn(self, fn_kind, fn_decl, block, span, node_id, ());
98102

99-
self.context.unsafe_context = old_unsafe_context
103+
self.unsafe_context = old_unsafe_context
100104
}
101105

102-
fn visit_block(&mut self, block:&Block, _:()) {
103-
104-
let old_unsafe_context = self.context.unsafe_context;
105-
let is_unsafe = match block.rules {
106-
ast::UnsafeBlock(*) => true, ast::DefaultBlock => false
107-
};
108-
if is_unsafe && self.context.unsafe_context == SafeContext {
109-
self.context.unsafe_context = UnsafeBlock(block.id)
110-
}
106+
fn visit_block(&mut self, block: &ast::Block, _:()) {
107+
let old_unsafe_context = self.unsafe_context;
108+
let is_unsafe = match block.rules {
109+
ast::UnsafeBlock(*) => true, ast::DefaultBlock => false
110+
};
111+
if is_unsafe && self.unsafe_context == SafeContext {
112+
self.unsafe_context = UnsafeBlock(block.id)
113+
}
111114

112-
visit::walk_block(self, block, ());
115+
visit::walk_block(self, block, ());
113116

114-
self.context.unsafe_context = old_unsafe_context
117+
self.unsafe_context = old_unsafe_context
115118
}
116119

117-
fn visit_expr(&mut self, expr:@Expr, _:()) {
118-
119-
match expr.node {
120-
ExprMethodCall(callee_id, _, _, _, _, _) => {
121-
let base_type = ty::node_id_to_type(self.tcx, callee_id);
122-
debug2!("effect: method call case, base type is {}",
123-
ppaux::ty_to_str(self.tcx, base_type));
124-
if type_is_unsafe_function(base_type) {
125-
self.require_unsafe(expr.span,
126-
"invocation of unsafe method")
127-
}
120+
fn visit_expr(&mut self, expr: @ast::Expr, _:()) {
121+
match expr.node {
122+
ast::ExprMethodCall(callee_id, _, _, _, _, _) => {
123+
let base_type = ty::node_id_to_type(self.tcx, callee_id);
124+
debug2!("effect: method call case, base type is {}",
125+
ppaux::ty_to_str(self.tcx, base_type));
126+
if type_is_unsafe_function(base_type) {
127+
self.require_unsafe(expr.span,
128+
"invocation of unsafe method")
128129
}
129-
ExprCall(base, _, _) => {
130-
let base_type = ty::node_id_to_type(self.tcx, base.id);
131-
debug2!("effect: call case, base type is {}",
132-
ppaux::ty_to_str(self.tcx, base_type));
133-
if type_is_unsafe_function(base_type) {
134-
self.require_unsafe(expr.span, "call to unsafe function")
135-
}
130+
}
131+
ast::ExprCall(base, _, _) => {
132+
let base_type = ty::node_id_to_type(self.tcx, base.id);
133+
debug2!("effect: call case, base type is {}",
134+
ppaux::ty_to_str(self.tcx, base_type));
135+
if type_is_unsafe_function(base_type) {
136+
self.require_unsafe(expr.span, "call to unsafe function")
136137
}
137-
ExprUnary(_, UnDeref, base) => {
138-
let base_type = ty::node_id_to_type(self.tcx, base.id);
139-
debug2!("effect: unary case, base type is {}",
140-
ppaux::ty_to_str(self.tcx, base_type));
141-
match ty::get(base_type).sty {
142-
ty_ptr(_) => {
143-
self.require_unsafe(expr.span,
144-
"dereference of unsafe pointer")
145-
}
146-
_ => {}
138+
}
139+
ast::ExprUnary(_, ast::UnDeref, base) => {
140+
let base_type = ty::node_id_to_type(self.tcx, base.id);
141+
debug2!("effect: unary case, base type is {}",
142+
ppaux::ty_to_str(self.tcx, base_type));
143+
match ty::get(base_type).sty {
144+
ty::ty_ptr(_) => {
145+
self.require_unsafe(expr.span,
146+
"dereference of unsafe pointer")
147147
}
148+
_ => {}
148149
}
149-
ExprInlineAsm(*) => {
150-
self.require_unsafe(expr.span, "use of inline assembly")
151-
}
152-
ExprPath(*) => {
153-
match ty::resolve_expr(self.tcx, expr) {
154-
ast::DefStatic(_, true) => {
155-
self.require_unsafe(expr.span, "use of mutable static")
156-
}
157-
_ => {}
150+
}
151+
ast::ExprAssign(base, _) | ast::ExprAssignOp(_, _, base, _) => {
152+
self.check_str_index(base);
153+
}
154+
ast::ExprAddrOf(ast::MutMutable, base) => {
155+
self.check_str_index(base);
156+
}
157+
ast::ExprInlineAsm(*) => {
158+
self.require_unsafe(expr.span, "use of inline assembly")
159+
}
160+
ast::ExprPath(*) => {
161+
match ty::resolve_expr(self.tcx, expr) {
162+
ast::DefStatic(_, true) => {
163+
self.require_unsafe(expr.span, "use of mutable static")
158164
}
165+
_ => {}
159166
}
160-
_ => {}
161167
}
168+
_ => {}
169+
}
162170

163-
visit::walk_expr(self, expr, ());
171+
visit::walk_expr(self, expr, ());
164172
}
165173
}
166174

167175
pub fn check_crate(tcx: ty::ctxt,
168176
method_map: method_map,
169177
crate: &ast::Crate) {
170-
let context = @mut Context {
171-
method_map: method_map,
172-
unsafe_context: SafeContext,
173-
};
174-
175178
let mut visitor = EffectCheckVisitor {
176179
tcx: tcx,
177-
context: context,
180+
method_map: method_map,
181+
unsafe_context: SafeContext,
178182
};
179183

180184
visit::walk_crate(&mut visitor, crate, ());
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 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.
4+
//
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.
10+
11+
fn main() {
12+
let mut s = ~"test";
13+
s[0] = 3; //~ ERROR: not allowed
14+
s[0] += 3; //~ ERROR: not allowed
15+
{
16+
let _a = &mut s[0]; //~ ERROR: not allowed
17+
}
18+
}

0 commit comments

Comments
 (0)