-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Don't require unsafe blocks for Share mut items #13348
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,10 +17,11 @@ use util::ppaux; | |
|
||
use syntax::ast; | ||
use syntax::codemap::Span; | ||
use syntax::print::pprust; | ||
use syntax::visit; | ||
use syntax::visit::Visitor; | ||
|
||
#[deriving(Eq)] | ||
#[deriving(Eq, Clone)] | ||
enum UnsafeContext { | ||
SafeContext, | ||
UnsafeFn, | ||
|
@@ -35,18 +36,26 @@ fn type_is_unsafe_function(ty: ty::t) -> bool { | |
} | ||
} | ||
|
||
#[deriving(Eq, Clone)] | ||
struct EffectEnv { | ||
/// Whether we're in an unsafe context. | ||
unsafe_context: UnsafeContext, | ||
|
||
/// Whether mut static usage should | ||
/// be forbidden regardless. | ||
allow_share: bool, | ||
} | ||
|
||
struct EffectCheckVisitor<'a> { | ||
tcx: &'a ty::ctxt, | ||
|
||
/// The method map. | ||
method_map: MethodMap, | ||
/// Whether we're in an unsafe context. | ||
unsafe_context: UnsafeContext, | ||
} | ||
|
||
impl<'a> EffectCheckVisitor<'a> { | ||
fn require_unsafe(&mut self, span: Span, description: &str) { | ||
match self.unsafe_context { | ||
fn require_unsafe(&mut self, span: Span, env: &EffectEnv, description: &str) { | ||
match env.unsafe_context { | ||
SafeContext => { | ||
// Report an error. | ||
self.tcx.sess.span_err(span, | ||
|
@@ -77,11 +86,18 @@ impl<'a> EffectCheckVisitor<'a> { | |
_ => {} | ||
} | ||
} | ||
|
||
fn expr_is_mut_static(&mut self, e: &ast::Expr) -> bool { | ||
match self.tcx.def_map.borrow().find(&e.id) { | ||
Some(&ast::DefStatic(_, true)) => true, | ||
_ => false | ||
} | ||
} | ||
} | ||
|
||
impl<'a> Visitor<()> for EffectCheckVisitor<'a> { | ||
impl<'a> Visitor<EffectEnv> for EffectCheckVisitor<'a> { | ||
fn visit_fn(&mut self, fn_kind: &visit::FnKind, fn_decl: &ast::FnDecl, | ||
block: &ast::Block, span: Span, node_id: ast::NodeId, _:()) { | ||
block: &ast::Block, span: Span, node_id: ast::NodeId, env: EffectEnv) { | ||
|
||
let (is_item_fn, is_unsafe_fn) = match *fn_kind { | ||
visit::FkItemFn(_, _, fn_style, _) => | ||
|
@@ -91,20 +107,19 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> { | |
_ => (false, false), | ||
}; | ||
|
||
let old_unsafe_context = self.unsafe_context; | ||
let mut env = env; | ||
|
||
if is_unsafe_fn { | ||
self.unsafe_context = UnsafeFn | ||
env.unsafe_context = UnsafeFn; | ||
} else if is_item_fn { | ||
self.unsafe_context = SafeContext | ||
env.unsafe_context = SafeContext; | ||
} | ||
|
||
visit::walk_fn(self, fn_kind, fn_decl, block, span, node_id, ()); | ||
|
||
self.unsafe_context = old_unsafe_context | ||
visit::walk_fn(self, fn_kind, fn_decl, block, span, node_id, env); | ||
} | ||
|
||
fn visit_block(&mut self, block: &ast::Block, _:()) { | ||
let old_unsafe_context = self.unsafe_context; | ||
fn visit_block(&mut self, block: &ast::Block, env: EffectEnv) { | ||
let mut env = env; | ||
match block.rules { | ||
ast::DefaultBlock => {} | ||
ast::UnsafeBlock(source) => { | ||
|
@@ -123,79 +138,131 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> { | |
// external blocks (e.g. `unsafe { println("") }`, | ||
// expands to `unsafe { ... unsafe { ... } }` where | ||
// the inner one is compiler generated). | ||
if self.unsafe_context == SafeContext || source == ast::CompilerGenerated { | ||
self.unsafe_context = UnsafeBlock(block.id) | ||
if env.unsafe_context == SafeContext || source == ast::CompilerGenerated { | ||
env.unsafe_context = UnsafeBlock(block.id); | ||
} | ||
} | ||
} | ||
|
||
visit::walk_block(self, block, ()); | ||
|
||
self.unsafe_context = old_unsafe_context | ||
visit::walk_block(self, block, env); | ||
} | ||
|
||
fn visit_expr(&mut self, expr: &ast::Expr, _:()) { | ||
fn visit_expr(&mut self, expr: &ast::Expr, env: EffectEnv) { | ||
let mut env = env; | ||
debug!("visit_expr(expr={}, allow_share={})", | ||
pprust::expr_to_str(expr), env.allow_share); | ||
match expr.node { | ||
ast::ExprMethodCall(_, _, _) => { | ||
ast::ExprMethodCall(_, _, ref args) => { | ||
let method_call = MethodCall::expr(expr.id); | ||
let base_type = self.method_map.borrow().get(&method_call).ty; | ||
debug!("effect: method call case, base type is {}", | ||
ppaux::ty_to_str(self.tcx, base_type)); | ||
if type_is_unsafe_function(base_type) { | ||
self.require_unsafe(expr.span, | ||
self.require_unsafe(expr.span, &env, | ||
"invocation of unsafe method") | ||
} | ||
|
||
// This is a method call, hence we just check the first | ||
// expression in the call args which corresponds `Self` | ||
if self.expr_is_mut_static(*args.get(0)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about |
||
let adj_ty = ty::expr_ty_adjusted(self.tcx, *args.get(0), | ||
&*self.method_map.borrow()); | ||
match ty::get(adj_ty).sty { | ||
ty::ty_rptr(_, mt) if mt.mutbl == ast::MutMutable => { | ||
self.require_unsafe(expr.span, &env, | ||
"mutable borrow of mutable static"); | ||
} | ||
_ => {} | ||
} | ||
} | ||
|
||
env.allow_share = true; | ||
} | ||
ast::ExprIndex(base, index) => { | ||
self.visit_expr(base, env.clone()); | ||
|
||
// It is safe to access share static mut | ||
// in index expressions. | ||
env.allow_share = true; | ||
return self.visit_expr(index, env); | ||
} | ||
ast::ExprCall(base, _) => { | ||
let base_type = ty::node_id_to_type(self.tcx, base.id); | ||
debug!("effect: call case, base type is {}", | ||
ppaux::ty_to_str(self.tcx, base_type)); | ||
if type_is_unsafe_function(base_type) { | ||
self.require_unsafe(expr.span, "call to unsafe function") | ||
self.require_unsafe(expr.span, &env, "call to unsafe function") | ||
} | ||
|
||
env.allow_share = true; | ||
} | ||
ast::ExprUnary(ast::UnDeref, base) => { | ||
let base_type = ty::node_id_to_type(self.tcx, base.id); | ||
debug!("effect: unary case, base type is {}", | ||
ppaux::ty_to_str(self.tcx, base_type)); | ||
match ty::get(base_type).sty { | ||
ty::ty_ptr(_) => { | ||
self.require_unsafe(expr.span, | ||
self.require_unsafe(expr.span, &env, | ||
"dereference of unsafe pointer") | ||
} | ||
_ => {} | ||
} | ||
} | ||
ast::ExprAssign(base, _) | ast::ExprAssignOp(_, base, _) => { | ||
self.check_str_index(base); | ||
ast::ExprAssign(lhs, rhs) | ast::ExprAssignOp(_, lhs, rhs) => { | ||
self.check_str_index(lhs); | ||
|
||
debug!("assign(rhs={}, lhs={})", | ||
pprust::expr_to_str(rhs), | ||
pprust::expr_to_str(lhs)) | ||
|
||
env.allow_share = true; | ||
self.visit_expr(rhs, env.clone()); | ||
|
||
// we want to ignore `Share` statics | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about static mut SHARABLE: *mut int = ...;
*SOME_SHARE = 1
static mut ANOTHER_SHARE: ... = ...;
*ANOTHER_SHARE.returns_mut_ref_safely() = 1; It's a little weird having indexing-on-the-LHS-of-assign validated by code that's well removed from the assignment code. |
||
// *just* in the LHS of the assignment. | ||
env.allow_share = false; | ||
return self.visit_expr(lhs, env); | ||
} | ||
ast::ExprAddrOf(ast::MutImmutable, _) => { | ||
env.allow_share = true; | ||
} | ||
ast::ExprAddrOf(ast::MutMutable, base) => { | ||
if self.expr_is_mut_static(base) { | ||
self.require_unsafe(expr.span, &env, | ||
"mutable borrow of mutable static"); | ||
} | ||
|
||
self.check_str_index(base); | ||
env.allow_share = true; | ||
} | ||
ast::ExprInlineAsm(..) => { | ||
self.require_unsafe(expr.span, "use of inline assembly") | ||
self.require_unsafe(expr.span, &env, "use of inline assembly") | ||
} | ||
ast::ExprPath(..) => { | ||
match ty::resolve_expr(self.tcx, expr) { | ||
ast::DefStatic(_, true) => { | ||
self.require_unsafe(expr.span, "use of mutable static") | ||
if self.expr_is_mut_static(expr) { | ||
let ety = ty::node_id_to_type(self.tcx, expr.id); | ||
if !env.allow_share || !ty::type_is_sharable(self.tcx, ety) { | ||
self.require_unsafe(expr.span, &env, "this use of mutable static"); | ||
} | ||
_ => {} | ||
} | ||
} | ||
_ => {} | ||
} | ||
|
||
visit::walk_expr(self, expr, ()); | ||
visit::walk_expr(self, expr, env); | ||
} | ||
} | ||
|
||
pub fn check_crate(tcx: &ty::ctxt, method_map: MethodMap, krate: &ast::Crate) { | ||
let mut visitor = EffectCheckVisitor { | ||
tcx: tcx, | ||
method_map: method_map, | ||
}; | ||
|
||
let env = EffectEnv{ | ||
allow_share: false, | ||
unsafe_context: SafeContext, | ||
}; | ||
|
||
visit::walk_crate(&mut visitor, krate, ()); | ||
visit::walk_crate(&mut visitor, krate, env); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Write
mut env: EffectEnv
in the function signature.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right (facepalm)