Skip to content

Commit 4c62486

Browse files
committed
rustc: Allow safe access of shareable static muts
Taking a shareable loan of a `static mut` is safe if the type contained in the location is ascribes to `Share`. This commit removes the need to have an `unsafe` block in these situations. Unsafe blocks are still required to write to or take mutable loans out of statics. An example of code that no longer requires unsafe code is: use std::sync::atomics; static mut CNT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; let cnt = CNT.fetch_add(1, atomics::SeqCst); As a consequence of this change, this code is not permitted: static mut FOO: uint = 30; println!("{}", FOO); The type `uint` is `Share`, and the static only has a shareable loan taken out on it, so the access does not require an unsafe block. Writes to the static are still unsafe, however, as they can invoke undefined behavior if not properly synchronized. Closes #13232
1 parent b495933 commit 4c62486

File tree

5 files changed

+214
-4
lines changed

5 files changed

+214
-4
lines changed

src/librustc/middle/effect.rs

+109-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ use middle::def;
1515
use middle::ty;
1616
use middle::typeck::MethodCall;
1717
use util::ppaux;
18+
use util::nodemap::NodeSet;
19+
use euv = middle::expr_use_visitor;
20+
use mc = middle::mem_categorization;
1821

1922
use syntax::ast;
2023
use syntax::ast_util::PostExpansionMethod;
@@ -40,10 +43,18 @@ fn type_is_unsafe_function(ty: ty::t) -> bool {
4043
struct EffectCheckVisitor<'a> {
4144
tcx: &'a ty::ctxt,
4245

46+
mutably_accessed_statics: &'a mut NodeSet,
47+
4348
/// Whether we're in an unsafe context.
4449
unsafe_context: UnsafeContext,
4550
}
4651

52+
struct FunctionVisitor<'a, 'b>(euv::ExprUseVisitor<'a, 'b, ty::ctxt>);
53+
54+
struct StaticMutChecker<'a> {
55+
mutably_accessed_statics: NodeSet,
56+
}
57+
4758
impl<'a> EffectCheckVisitor<'a> {
4859
fn require_unsafe(&mut self, span: Span, description: &str) {
4960
match self.unsafe_context {
@@ -142,6 +153,10 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> {
142153
}
143154

144155
fn visit_expr(&mut self, expr: &ast::Expr, _:()) {
156+
if self.mutably_accessed_statics.remove(&expr.id) {
157+
self.require_unsafe(expr.span, "mutable use of static")
158+
}
159+
145160
match expr.node {
146161
ast::ExprMethodCall(_, _, _) => {
147162
let method_call = MethodCall::expr(expr.id);
@@ -185,7 +200,12 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> {
185200
ast::ExprPath(..) => {
186201
match ty::resolve_expr(self.tcx, expr) {
187202
def::DefStatic(_, true) => {
188-
self.require_unsafe(expr.span, "use of mutable static")
203+
let ty = ty::node_id_to_type(self.tcx, expr.id);
204+
let contents = ty::type_contents(self.tcx, ty);
205+
if !contents.is_sharable(self.tcx) {
206+
self.require_unsafe(expr.span,
207+
"use of non-Share static mut")
208+
}
189209
}
190210
_ => {}
191211
}
@@ -197,11 +217,98 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> {
197217
}
198218
}
199219

220+
impl<'a, 'b> Visitor<()> for FunctionVisitor<'a, 'b> {
221+
fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl,
222+
b: &ast::Block, s: Span, _: ast::NodeId, _: ()) {
223+
{
224+
let FunctionVisitor(ref mut inner) = *self;
225+
inner.walk_fn(fd, b);
226+
}
227+
visit::walk_fn(self, fk, fd, b, s, ());
228+
}
229+
}
230+
231+
impl<'a> StaticMutChecker<'a> {
232+
fn is_static_mut(&self, mut cur: &mc::cmt) -> bool {
233+
loop {
234+
match cur.cat {
235+
mc::cat_static_item => {
236+
return match cur.mutbl {
237+
mc::McImmutable => return false,
238+
_ => true
239+
}
240+
}
241+
mc::cat_deref(ref cmt, _, _) |
242+
mc::cat_discr(ref cmt, _) |
243+
mc::cat_downcast(ref cmt) |
244+
mc::cat_interior(ref cmt, _) => cur = cmt,
245+
246+
mc::cat_rvalue(..) |
247+
mc::cat_copied_upvar(..) |
248+
mc::cat_upvar(..) |
249+
mc::cat_local(..) |
250+
mc::cat_arg(..) => return false
251+
}
252+
}
253+
}
254+
}
255+
256+
impl<'a> euv::Delegate for StaticMutChecker<'a> {
257+
fn borrow(&mut self,
258+
borrow_id: ast::NodeId,
259+
_borrow_span: Span,
260+
cmt: mc::cmt,
261+
_loan_region: ty::Region,
262+
bk: ty::BorrowKind,
263+
_loan_cause: euv::LoanCause) {
264+
if !self.is_static_mut(&cmt) {
265+
return
266+
}
267+
match bk {
268+
ty::ImmBorrow => {}
269+
ty::UniqueImmBorrow | ty::MutBorrow => {
270+
self.mutably_accessed_statics.insert(borrow_id);
271+
}
272+
}
273+
}
274+
275+
fn mutate(&mut self,
276+
assignment_id: ast::NodeId,
277+
_assignment_span: Span,
278+
assignee_cmt: mc::cmt,
279+
_mode: euv::MutateMode) {
280+
if !self.is_static_mut(&assignee_cmt) {
281+
return
282+
}
283+
self.mutably_accessed_statics.insert(assignment_id);
284+
}
285+
286+
fn consume(&mut self,
287+
_consume_id: ast::NodeId,
288+
_consume_span: Span,
289+
_cmt: mc::cmt,
290+
_mode: euv::ConsumeMode) {}
291+
fn consume_pat(&mut self,
292+
_consume_pat: &ast::Pat,
293+
_cmt: mc::cmt,
294+
_mode: euv::ConsumeMode) {}
295+
fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {}
296+
}
297+
200298
pub fn check_crate(tcx: &ty::ctxt, krate: &ast::Crate) {
299+
let mut delegate = StaticMutChecker {
300+
mutably_accessed_statics: NodeSet::new(),
301+
};
302+
{
303+
let visitor = euv::ExprUseVisitor::new(&mut delegate, tcx);
304+
visit::walk_crate(&mut FunctionVisitor(visitor), krate, ());
305+
}
306+
201307
let mut visitor = EffectCheckVisitor {
202308
tcx: tcx,
203309
unsafe_context: SafeContext,
310+
mutably_accessed_statics: &mut delegate.mutably_accessed_statics,
204311
};
205-
206312
visit::walk_crate(&mut visitor, krate, ());
313+
assert!(visitor.mutably_accessed_statics.len() == 0);
207314
}

src/test/compile-fail/static-mut-foreign-requires-unsafe.rs

-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,4 @@ extern {
1717
fn main() {
1818
a += 3; //~ ERROR: requires unsafe
1919
a = 4; //~ ERROR: requires unsafe
20-
let _b = a; //~ ERROR: requires unsafe
2120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2014 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+
use std::kinds::marker;
12+
13+
struct NonSharable {
14+
field: uint,
15+
noshare: marker::NoShare
16+
}
17+
18+
struct Sharable {
19+
field: uint
20+
}
21+
22+
impl Sharable {
23+
fn foo(&self) {}
24+
fn foo_mut(&mut self) {}
25+
}
26+
27+
static mut NON_SHARABLE: NonSharable = NonSharable {
28+
field: 1,
29+
noshare: marker::NoShare,
30+
};
31+
32+
static mut SHARABLE: Sharable = Sharable { field: 0 };
33+
34+
pub fn fn_mut(_: &mut Sharable) {}
35+
36+
pub fn main() {
37+
SHARABLE.foo();
38+
39+
SHARABLE.foo_mut();
40+
//~^ ERROR: mutable use of static requires unsafe function or block
41+
42+
SHARABLE.field = 2;
43+
//~^ ERROR: mutable use of static requires unsafe function or block
44+
45+
fn_mut(&mut SHARABLE);
46+
//~^ ERROR mutable use of static requires unsafe function or block
47+
48+
NON_SHARABLE.field = 2;
49+
//~^ ERROR: use of non-Share static mut requires unsafe function or block
50+
//~^^ ERROR: mutable use of static requires unsafe function or block
51+
52+
SHARABLE = Sharable {field: 1};
53+
//~^ ERROR: mutable use of static requires unsafe function or block
54+
55+
let _: &mut Sharable = &mut SHARABLE;
56+
//~^ ERROR mutable use of static requires unsafe function or block
57+
58+
let _ = &NON_SHARABLE.field;
59+
//~^ ERROR: use of non-Share static mut requires unsafe function or block
60+
61+
let mut slc = ['a', 'c'];
62+
slc[NON_SHARABLE.field] = 'b';
63+
//~^ ERROR: use of non-Share static mut requires unsafe function or block
64+
65+
slc[SHARABLE.field] = 'b';
66+
}

src/test/compile-fail/static-mut-requires-unsafe.rs

-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@ static mut a: int = 3;
1313
fn main() {
1414
a += 3; //~ ERROR: requires unsafe
1515
a = 4; //~ ERROR: requires unsafe
16-
let _b = a; //~ ERROR: requires unsafe
1716
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2014 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+
struct Sharable {
12+
field: uint
13+
}
14+
15+
impl Sharable {
16+
fn foo(&self) {}
17+
fn foo_mut(&mut self) {}
18+
}
19+
20+
static mut FOO: Sharable = Sharable { field: 1 };
21+
22+
fn borrow_static(_: &Sharable) {}
23+
24+
pub fn main() {
25+
26+
FOO.foo();
27+
28+
borrow_static(&FOO);
29+
30+
let _ = &FOO;
31+
32+
unsafe { let _: &mut Sharable = &mut FOO; }
33+
34+
let mut slc = ['a', 'c'];
35+
slc[FOO.field] = 'b';
36+
37+
let _ = &((((FOO))));
38+
}
39+

0 commit comments

Comments
 (0)