2013-05-23 21:12:16 -05:00
|
|
|
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! Enforces the Rust effect system. Currently there is just one effect,
|
|
|
|
/// `unsafe`.
|
2014-11-06 02:05:53 -06:00
|
|
|
use self::UnsafeContext::*;
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-05-14 14:31:30 -05:00
|
|
|
use middle::def;
|
2014-09-13 13:09:25 -05:00
|
|
|
use middle::ty::{mod, Ty};
|
2014-11-25 13:21:20 -06:00
|
|
|
use middle::ty::MethodCall;
|
2013-05-23 21:12:16 -05:00
|
|
|
use util::ppaux;
|
|
|
|
|
|
|
|
use syntax::ast;
|
2014-07-14 18:31:52 -05:00
|
|
|
use syntax::ast_util::PostExpansionMethod;
|
2013-08-31 11:13:04 -05:00
|
|
|
use syntax::codemap::Span;
|
2013-08-12 21:15:54 -05:00
|
|
|
use syntax::visit;
|
2013-10-07 16:25:30 -05:00
|
|
|
use syntax::visit::Visitor;
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-05-29 19:45:07 -05:00
|
|
|
#[deriving(PartialEq)]
|
2013-05-23 21:12:16 -05:00
|
|
|
enum UnsafeContext {
|
|
|
|
SafeContext,
|
|
|
|
UnsafeFn,
|
2013-07-27 03:25:59 -05:00
|
|
|
UnsafeBlock(ast::NodeId),
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|
|
|
|
|
librustc: Make `Copy` opt-in.
This change makes the compiler no longer infer whether types (structures
and enumerations) implement the `Copy` trait (and thus are implicitly
copyable). Rather, you must implement `Copy` yourself via `impl Copy for
MyType {}`.
A new warning has been added, `missing_copy_implementations`, to warn
you if a non-generic public type has been added that could have
implemented `Copy` but didn't.
For convenience, you may *temporarily* opt out of this behavior by using
`#![feature(opt_out_copy)]`. Note though that this feature gate will never be
accepted and will be removed by the time that 1.0 is released, so you should
transition your code away from using it.
This breaks code like:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
Change this code to:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
impl Copy for Point2D {}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
This is the backwards-incompatible part of #13231.
Part of RFC #3.
[breaking-change]
2014-12-05 19:01:33 -06:00
|
|
|
impl Copy for UnsafeContext {}
|
|
|
|
|
2014-09-13 13:09:25 -05:00
|
|
|
fn type_is_unsafe_function(ty: Ty) -> bool {
|
2014-10-31 03:51:16 -05:00
|
|
|
match ty.sty {
|
2014-12-09 09:36:46 -06:00
|
|
|
ty::ty_bare_fn(ref f) => f.unsafety == ast::Unsafety::Unsafe,
|
|
|
|
ty::ty_closure(ref f) => f.unsafety == ast::Unsafety::Unsafe,
|
2013-05-23 21:12:16 -05:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct EffectCheckVisitor<'a, 'tcx: 'a> {
|
|
|
|
tcx: &'a ty::ctxt<'tcx>,
|
2013-10-07 16:25:30 -05:00
|
|
|
|
|
|
|
/// Whether we're in an unsafe context.
|
|
|
|
unsafe_context: UnsafeContext,
|
2013-08-12 21:15:54 -05:00
|
|
|
}
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> EffectCheckVisitor<'a, 'tcx> {
|
2013-08-31 11:13:04 -05:00
|
|
|
fn require_unsafe(&mut self, span: Span, description: &str) {
|
2013-10-07 16:25:30 -05:00
|
|
|
match self.unsafe_context {
|
2013-05-23 21:12:16 -05:00
|
|
|
SafeContext => {
|
|
|
|
// Report an error.
|
2014-07-17 12:56:37 -05:00
|
|
|
span_err!(self.tcx.sess, span, E0133,
|
|
|
|
"{} requires unsafe function or block",
|
|
|
|
description);
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|
|
|
|
UnsafeBlock(block_id) => {
|
|
|
|
// OK, but record this.
|
2014-10-15 01:25:34 -05:00
|
|
|
debug!("effect: recording unsafe block as used: {}", block_id);
|
2014-03-20 21:49:20 -05:00
|
|
|
self.tcx.used_unsafe.borrow_mut().insert(block_id);
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|
|
|
|
UnsafeFn => {}
|
|
|
|
}
|
2013-08-12 21:15:54 -05:00
|
|
|
}
|
2013-10-07 16:25:30 -05:00
|
|
|
|
2014-05-16 12:15:33 -05:00
|
|
|
fn check_str_index(&mut self, e: &ast::Expr) {
|
2013-10-07 16:25:30 -05:00
|
|
|
let base_type = match e.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprIndex(ref base, _) => ty::node_id_to_type(self.tcx, base.id),
|
2013-10-07 16:25:30 -05:00
|
|
|
_ => return
|
|
|
|
};
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("effect: checking index with base type {}",
|
2014-06-21 05:39:03 -05:00
|
|
|
ppaux::ty_to_string(self.tcx, base_type));
|
2014-10-31 03:51:16 -05:00
|
|
|
match base_type.sty {
|
2014-11-29 15:41:21 -06:00
|
|
|
ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) => if ty::ty_str == ty.sty {
|
|
|
|
span_err!(self.tcx.sess, e.span, E0134,
|
|
|
|
"modification of string types is not allowed");
|
2014-04-26 17:19:15 -05:00
|
|
|
},
|
2014-04-28 20:10:23 -05:00
|
|
|
ty::ty_str => {
|
2014-07-17 12:56:37 -05:00
|
|
|
span_err!(self.tcx.sess, e.span, E0135,
|
|
|
|
"modification of string types is not allowed");
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
2013-08-12 21:15:54 -05:00
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
|
|
|
|
fn visit_fn(&mut self, fn_kind: visit::FnKind<'v>, fn_decl: &'v ast::FnDecl,
|
|
|
|
block: &'v ast::Block, span: Span, _: ast::NodeId) {
|
2013-10-07 16:25:30 -05:00
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
let (is_item_fn, is_unsafe_fn) = match fn_kind {
|
2014-04-06 20:04:40 -05:00
|
|
|
visit::FkItemFn(_, _, fn_style, _) =>
|
2014-12-09 09:36:46 -06:00
|
|
|
(true, fn_style == ast::Unsafety::Unsafe),
|
2014-01-09 07:05:33 -06:00
|
|
|
visit::FkMethod(_, _, method) =>
|
2014-12-09 09:36:46 -06:00
|
|
|
(true, method.pe_unsafety() == ast::Unsafety::Unsafe),
|
2013-10-07 16:25:30 -05:00
|
|
|
_ => (false, false),
|
|
|
|
};
|
|
|
|
|
|
|
|
let old_unsafe_context = self.unsafe_context;
|
|
|
|
if is_unsafe_fn {
|
|
|
|
self.unsafe_context = UnsafeFn
|
|
|
|
} else if is_item_fn {
|
|
|
|
self.unsafe_context = SafeContext
|
|
|
|
}
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_fn(self, fn_kind, fn_decl, block, span);
|
2013-08-12 21:15:54 -05:00
|
|
|
|
2013-10-07 16:25:30 -05:00
|
|
|
self.unsafe_context = old_unsafe_context
|
2013-08-12 21:15:54 -05:00
|
|
|
}
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_block(&mut self, block: &ast::Block) {
|
2013-10-07 16:25:30 -05:00
|
|
|
let old_unsafe_context = self.unsafe_context;
|
2014-02-20 06:22:45 -06:00
|
|
|
match block.rules {
|
|
|
|
ast::DefaultBlock => {}
|
|
|
|
ast::UnsafeBlock(source) => {
|
|
|
|
// By default only the outermost `unsafe` block is
|
|
|
|
// "used" and so nested unsafe blocks are pointless
|
|
|
|
// (the inner ones are unnecessary and we actually
|
|
|
|
// warn about them). As such, there are two cases when
|
|
|
|
// we need to create a new context, when we're
|
|
|
|
// - outside `unsafe` and found a `unsafe` block
|
|
|
|
// (normal case)
|
2014-08-23 05:41:32 -05:00
|
|
|
// - inside `unsafe`, found an `unsafe` block
|
2014-02-20 06:22:45 -06:00
|
|
|
// created internally to the compiler
|
|
|
|
//
|
|
|
|
// The second case is necessary to ensure that the
|
|
|
|
// compiler `unsafe` blocks don't accidentally "use"
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
}
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_block(self, block);
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2013-10-07 16:25:30 -05:00
|
|
|
self.unsafe_context = old_unsafe_context
|
2013-08-12 21:15:54 -05:00
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_expr(&mut self, expr: &ast::Expr) {
|
2013-10-07 16:25:30 -05:00
|
|
|
match expr.node {
|
2014-02-26 08:06:45 -06:00
|
|
|
ast::ExprMethodCall(_, _, _) => {
|
2014-03-06 11:24:11 -06:00
|
|
|
let method_call = MethodCall::expr(expr.id);
|
2014-10-15 01:05:01 -05:00
|
|
|
let base_type = (*self.tcx.method_map.borrow())[method_call].ty;
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("effect: method call case, base type is {}",
|
2014-06-21 05:39:03 -05:00
|
|
|
ppaux::ty_to_string(self.tcx, base_type));
|
2013-10-07 16:25:30 -05:00
|
|
|
if type_is_unsafe_function(base_type) {
|
|
|
|
self.require_unsafe(expr.span,
|
|
|
|
"invocation of unsafe method")
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprCall(ref base, _) => {
|
2013-10-07 16:25:30 -05:00
|
|
|
let base_type = ty::node_id_to_type(self.tcx, base.id);
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("effect: call case, base type is {}",
|
2014-06-21 05:39:03 -05:00
|
|
|
ppaux::ty_to_string(self.tcx, base_type));
|
2013-10-07 16:25:30 -05:00
|
|
|
if type_is_unsafe_function(base_type) {
|
|
|
|
self.require_unsafe(expr.span, "call to unsafe function")
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprUnary(ast::UnDeref, ref base) => {
|
2013-10-07 16:25:30 -05:00
|
|
|
let base_type = ty::node_id_to_type(self.tcx, base.id);
|
2013-10-21 15:08:31 -05:00
|
|
|
debug!("effect: unary case, base type is {}",
|
2014-11-29 15:41:21 -06:00
|
|
|
ppaux::ty_to_string(self.tcx, base_type));
|
|
|
|
if let ty::ty_ptr(_) = base_type.sty {
|
|
|
|
self.require_unsafe(expr.span, "dereference of unsafe pointer")
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprAssign(ref base, _) | ast::ExprAssignOp(_, ref base, _) => {
|
|
|
|
self.check_str_index(&**base);
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
2014-05-16 12:15:33 -05:00
|
|
|
ast::ExprAddrOf(ast::MutMutable, ref base) => {
|
|
|
|
self.check_str_index(&**base);
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
2013-11-28 14:22:53 -06:00
|
|
|
ast::ExprInlineAsm(..) => {
|
2014-11-29 15:41:21 -06:00
|
|
|
self.require_unsafe(expr.span, "use of inline assembly");
|
2013-10-07 16:25:30 -05:00
|
|
|
}
|
2013-11-28 14:22:53 -06:00
|
|
|
ast::ExprPath(..) => {
|
2014-11-29 15:41:21 -06:00
|
|
|
if let def::DefStatic(_, true) = ty::resolve_expr(self.tcx, expr) {
|
|
|
|
self.require_unsafe(expr.span, "use of mutable static");
|
2013-06-21 20:46:34 -05:00
|
|
|
}
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|
2013-10-07 16:25:30 -05:00
|
|
|
_ => {}
|
|
|
|
}
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_expr(self, expr);
|
2013-08-12 21:15:54 -05:00
|
|
|
}
|
|
|
|
}
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-09-07 12:09:06 -05:00
|
|
|
pub fn check_crate(tcx: &ty::ctxt) {
|
2013-08-12 21:15:54 -05:00
|
|
|
let mut visitor = EffectCheckVisitor {
|
|
|
|
tcx: tcx,
|
2013-10-07 16:25:30 -05:00
|
|
|
unsafe_context: SafeContext,
|
2013-08-12 21:15:54 -05:00
|
|
|
};
|
2013-05-23 21:12:16 -05:00
|
|
|
|
2014-09-07 12:09:06 -05:00
|
|
|
visit::walk_crate(&mut visitor, tcx.map.krate());
|
2013-05-23 21:12:16 -05:00
|
|
|
}
|