From 81533ff737f2d71c7518188b71fbafd73e0d1434 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Oct 2011 17:31:23 -0700 Subject: [PATCH] add pass to check that unsafe fns cannot be used as values --- src/comp/driver/rustc.rs | 4 +- src/comp/middle/unsafeck.rs | 72 +++++++++++++++++++ src/comp/rustc.rc | 1 + .../compile-fail/unsafe-fn-used-as-value.rs | 9 +++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/comp/middle/unsafeck.rs create mode 100644 src/test/compile-fail/unsafe-fn-used-as-value.rs diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index 76819b996054..84409978bda3 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -5,7 +5,7 @@ import metadata::{creader, cstore}; import syntax::parse::{parser, token}; import syntax::{ast, codemap}; import front::attr; -import middle::{trans, resolve, freevars, kind, ty, typeck}; +import middle::{trans, resolve, freevars, kind, ty, typeck, unsafeck}; import middle::tstate::ck; import syntax::print::{pp, pprust}; import util::{ppaux, common, filesearch}; @@ -129,6 +129,8 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str, bind freevars::annotate_freevars(def_map, crate)); let ty_cx = ty::mk_ctxt(sess, def_map, ext_map, ast_map, freevars); time(time_passes, "typechecking", bind typeck::check_crate(ty_cx, crate)); + time(time_passes, "unsafechecking", + bind unsafeck::unsafeck_crate(ty_cx, crate)); time(time_passes, "alt checking", bind middle::check_alt::check_crate(ty_cx, crate)); if sess.get_opts().run_typestate { diff --git a/src/comp/middle/unsafeck.rs b/src/comp/middle/unsafeck.rs new file mode 100644 index 000000000000..251258f11a8b --- /dev/null +++ b/src/comp/middle/unsafeck.rs @@ -0,0 +1,72 @@ +import syntax::ast; +import syntax::visit; +import std::option::some; +import syntax::print::pprust::{expr_to_str, path_to_str}; + +export unsafeck_crate; + +type unsafe_ctx = { + tcx: ty::ctxt, + unsafe_fn_legal: bool +}; + +fn unsafeck_view_item(_vi: @ast::view_item, + _ctx: unsafe_ctx, + _v: visit::vt) { + // Ignore paths that appear in use, import, etc +} + +fn unsafeck_expr(expr: @ast::expr, + ctx: unsafe_ctx, + v: visit::vt) { + alt expr.node { + ast::expr_path(path) { + if !ctx.unsafe_fn_legal { + alt ctx.tcx.def_map.find(expr.id) { + some(ast::def_fn(_, ast::unsafe_fn.)) | + some(ast::def_native_fn(_, ast::unsafe_fn.)) { + log_err ("expr=", expr_to_str(expr)); + ctx.tcx.sess.span_fatal( + expr.span, + "unsafe functions can only be called"); + } + + _ {} + } + } + } + + ast::expr_call(f, args) { + let f_ctx = {unsafe_fn_legal: true with ctx}; + visit::visit_expr(f, f_ctx, v); + + let args_ctx = {unsafe_fn_legal: false with ctx}; + visit::visit_exprs(args, args_ctx, v); + } + + _ { + let subctx = {unsafe_fn_legal: false with ctx}; + visit::visit_expr(expr, subctx, v); + } + } +} + +fn unsafeck_crate(tcx: ty::ctxt, crate: @ast::crate) { + let visit = + visit::mk_vt( + @{visit_expr: unsafeck_expr, + visit_view_item: unsafeck_view_item + with *visit::default_visitor()}); + let ctx = {tcx: tcx, unsafe_fn_legal: false}; + visit::visit_crate(*crate, ctx, visit); +} + + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; +// End: diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 9b1dff757699..2f861155e1ca 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -25,6 +25,7 @@ mod middle { mod ast_map; mod resolve; mod typeck; + mod unsafeck; mod check_alt; mod mut; mod alias; diff --git a/src/test/compile-fail/unsafe-fn-used-as-value.rs b/src/test/compile-fail/unsafe-fn-used-as-value.rs new file mode 100644 index 000000000000..e2fa46b0374f --- /dev/null +++ b/src/test/compile-fail/unsafe-fn-used-as-value.rs @@ -0,0 +1,9 @@ +// -*- rust -*- +// error-pattern: unsafe functions can only be called + +unsafe fn f() { ret; } + +fn main() { + let x = f; + x(); +}