about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/compiletest/errors.rs52
-rw-r--r--src/compiletest/header.rs18
-rw-r--r--src/libcore/comm.rs18
-rw-r--r--src/libcore/io.rs23
-rw-r--r--src/libcore/managed.rs4
-rw-r--r--src/libcore/num/float.rs12
-rw-r--r--src/libcore/path.rs60
-rw-r--r--src/libcore/ptr.rs8
-rw-r--r--src/libcore/rt/context.rs4
-rw-r--r--src/libcore/rt/thread_local_storage.rs6
-rw-r--r--src/libcore/run.rs106
-rw-r--r--src/libcore/str.rs44
-rw-r--r--src/libcore/sys.rs12
-rw-r--r--src/libcore/task/mod.rs79
-rw-r--r--src/libcore/task/spawn.rs4
-rw-r--r--src/libcore/unstable.rs42
-rw-r--r--src/libcore/unstable/exchange_alloc.rs30
-rw-r--r--src/libcore/unstable/extfmt.rs12
-rw-r--r--src/libcore/unstable/lang.rs4
-rw-r--r--src/librustc/back/link.rs4
-rw-r--r--src/librustc/middle/borrowck/check_loans.rs120
-rw-r--r--src/librustc/middle/lang_items.rs2
-rw-r--r--src/librustc/middle/lint.rs57
-rw-r--r--src/librustc/middle/resolve.rs18
-rw-r--r--src/librustc/middle/trans/base.rs22
-rw-r--r--src/librustc/middle/trans/build.rs4
-rw-r--r--src/librustc/middle/trans/debuginfo.rs294
-rw-r--r--src/librustc/middle/ty.rs21
-rw-r--r--src/librustc/middle/typeck/check/mod.rs36
-rw-r--r--src/libstd/c_vec.rs14
-rw-r--r--src/libstd/getopts.rs218
-rw-r--r--src/libstd/net_ip.rs52
-rw-r--r--src/libstd/net_tcp.rs307
-rw-r--r--src/libstd/net_url.rs57
-rw-r--r--src/libstd/rl.rs34
-rw-r--r--src/libstd/rope.rs20
-rw-r--r--src/libstd/sync.rs66
-rw-r--r--src/libstd/timer.rs74
-rw-r--r--src/libstd/uv_iotask.rs6
-rw-r--r--src/libstd/uv_ll.rs12
-rw-r--r--src/libsyntax/abi.rs10
-rw-r--r--src/test/compile-fail/unused-unsafe.rs44
42 files changed, 1045 insertions, 985 deletions
diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs
index 63b5c64c6d4..5a5c091d957 100644
--- a/src/compiletest/errors.rs
+++ b/src/compiletest/errors.rs
@@ -30,36 +30,34 @@ pub fn load_errors(testfile: &Path) -> ~[ExpectedError] {
 }
 
 fn parse_expected(line_num: uint, line: ~str) -> ~[ExpectedError] {
-    unsafe {
-        let error_tag = ~"//~";
-        let mut idx;
-        match str::find_str(line, error_tag) {
-          None => return ~[],
-          Some(nn) => { idx = (nn as uint) + str::len(error_tag); }
-        }
+    let error_tag = ~"//~";
+    let mut idx;
+    match str::find_str(line, error_tag) {
+      None => return ~[],
+      Some(nn) => { idx = (nn as uint) + str::len(error_tag); }
+    }
 
-        // "//~^^^ kind msg" denotes a message expected
-        // three lines above current line:
-        let mut adjust_line = 0u;
-        let len = str::len(line);
-        while idx < len && line[idx] == ('^' as u8) {
-            adjust_line += 1u;
-            idx += 1u;
-        }
+    // "//~^^^ kind msg" denotes a message expected
+    // three lines above current line:
+    let mut adjust_line = 0u;
+    let len = str::len(line);
+    while idx < len && line[idx] == ('^' as u8) {
+        adjust_line += 1u;
+        idx += 1u;
+    }
 
-        // Extract kind:
-        while idx < len && line[idx] == (' ' as u8) { idx += 1u; }
-        let start_kind = idx;
-        while idx < len && line[idx] != (' ' as u8) { idx += 1u; }
-        let kind = str::to_lower(str::slice(line, start_kind, idx).to_owned());
+    // Extract kind:
+    while idx < len && line[idx] == (' ' as u8) { idx += 1u; }
+    let start_kind = idx;
+    while idx < len && line[idx] != (' ' as u8) { idx += 1u; }
+    let kind = str::to_lower(str::slice(line, start_kind, idx).to_owned());
 
-        // Extract msg:
-        while idx < len && line[idx] == (' ' as u8) { idx += 1u; }
-        let msg = str::slice(line, idx, len).to_owned();
+    // Extract msg:
+    while idx < len && line[idx] == (' ' as u8) { idx += 1u; }
+    let msg = str::slice(line, idx, len).to_owned();
 
-        debug!("line=%u kind=%s msg=%s", line_num - adjust_line, kind, msg);
+    debug!("line=%u kind=%s msg=%s", line_num - adjust_line, kind, msg);
 
-        return ~[ExpectedError{line: line_num - adjust_line, kind: kind,
-                               msg: msg}];
-    }
+    return ~[ExpectedError{line: line_num - adjust_line, kind: kind,
+                           msg: msg}];
 }
diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs
index 5a35c56c075..2b365188338 100644
--- a/src/compiletest/header.rs
+++ b/src/compiletest/header.rs
@@ -171,16 +171,14 @@ fn parse_name_directive(line: ~str, directive: ~str) -> bool {
 
 fn parse_name_value_directive(line: ~str,
                               directive: ~str) -> Option<~str> {
-    unsafe {
-        let keycolon = directive + ~":";
-        match str::find_str(line, keycolon) {
-            Some(colon) => {
-                let value = str::slice(line, colon + str::len(keycolon),
-                                       str::len(line)).to_owned();
-                debug!("%s: %s", directive,  value);
-                Some(value)
-            }
-            None => None
+    let keycolon = directive + ~":";
+    match str::find_str(line, keycolon) {
+        Some(colon) => {
+            let value = str::slice(line, colon + str::len(keycolon),
+                                   str::len(line)).to_owned();
+            debug!("%s: %s", directive,  value);
+            Some(value)
         }
+        None => None
     }
 }
diff --git a/src/libcore/comm.rs b/src/libcore/comm.rs
index 9fd6f1db793..d665bf311f3 100644
--- a/src/libcore/comm.rs
+++ b/src/libcore/comm.rs
@@ -188,16 +188,14 @@ impl<T: Owned> Peekable<T> for Port<T> {
 
 #[inline(always)]
 fn port_peek<T:Owned>(self: &Port<T>) -> bool {
-    unsafe {
-        let mut endp = None;
-        endp <-> self.endp;
-        let peek = match &endp {
-            &Some(ref endp) => peek(endp),
-            &None => fail!(~"peeking empty stream")
-        };
-        self.endp <-> endp;
-        peek
-    }
+    let mut endp = None;
+    endp <-> self.endp;
+    let peek = match &endp {
+        &Some(ref endp) => peek(endp),
+        &None => fail!(~"peeking empty stream")
+    };
+    self.endp <-> endp;
+    peek
 }
 
 impl<T: Owned> Selectable for Port<T> {
diff --git a/src/libcore/io.rs b/src/libcore/io.rs
index 3853e7c8f2b..3c5900f51a2 100644
--- a/src/libcore/io.rs
+++ b/src/libcore/io.rs
@@ -1536,11 +1536,8 @@ pub fn with_bytes_writer(f: &fn(@Writer)) -> ~[u8] {
 pub fn with_str_writer(f: &fn(@Writer)) -> ~str {
     let mut v = with_bytes_writer(f);
 
-    // FIXME (#3758): This should not be needed.
-    unsafe {
-        // Make sure the vector has a trailing null and is proper utf8.
-        v.push(0);
-    }
+    // Make sure the vector has a trailing null and is proper utf8.
+    v.push(0);
     assert!(str::is_utf8(v));
 
     unsafe { ::cast::transmute(v) }
@@ -1640,16 +1637,14 @@ pub mod fsync {
     // outer res
     pub fn FILE_res_sync(file: &FILERes, opt_level: Option<Level>,
                          blk: &fn(v: Res<*libc::FILE>)) {
-        unsafe {
-            blk(Res(Arg {
-                val: file.f, opt_level: opt_level,
-                fsync_fn: |file, l| {
-                    unsafe {
-                        os::fsync_fd(libc::fileno(file), l) as int
-                    }
+        blk(Res(Arg {
+            val: file.f, opt_level: opt_level,
+            fsync_fn: |file, l| {
+                unsafe {
+                    os::fsync_fd(libc::fileno(file), l) as int
                 }
-            }));
-        }
+            }
+        }));
     }
 
     // fsync fd after executing blk
diff --git a/src/libcore/managed.rs b/src/libcore/managed.rs
index 234b710d238..2bd3959acf4 100644
--- a/src/libcore/managed.rs
+++ b/src/libcore/managed.rs
@@ -38,13 +38,13 @@ pub mod raw {
 #[inline(always)]
 pub fn ptr_eq<T>(a: @T, b: @T) -> bool {
     //! Determine if two shared boxes point to the same object
-    unsafe { ptr::addr_of(&(*a)) == ptr::addr_of(&(*b)) }
+    ptr::addr_of(&(*a)) == ptr::addr_of(&(*b))
 }
 
 #[inline(always)]
 pub fn mut_ptr_eq<T>(a: @mut T, b: @mut T) -> bool {
     //! Determine if two mutable shared boxes point to the same object
-    unsafe { ptr::addr_of(&(*a)) == ptr::addr_of(&(*b)) }
+    ptr::addr_of(&(*a)) == ptr::addr_of(&(*b))
 }
 
 #[cfg(notest)]
diff --git a/src/libcore/num/float.rs b/src/libcore/num/float.rs
index 1ab0e24f62d..87d04b05087 100644
--- a/src/libcore/num/float.rs
+++ b/src/libcore/num/float.rs
@@ -369,27 +369,27 @@ pub fn is_NaN(x: float) -> bool { f64::is_NaN(x as f64) }
 
 #[inline(always)]
 pub fn abs(x: float) -> float {
-    unsafe { f64::abs(x as f64) as float }
+    f64::abs(x as f64) as float
 }
 #[inline(always)]
 pub fn sqrt(x: float) -> float {
-    unsafe { f64::sqrt(x as f64) as float }
+    f64::sqrt(x as f64) as float
 }
 #[inline(always)]
 pub fn atan(x: float) -> float {
-    unsafe { f64::atan(x as f64) as float }
+    f64::atan(x as f64) as float
 }
 #[inline(always)]
 pub fn sin(x: float) -> float {
-    unsafe { f64::sin(x as f64) as float }
+    f64::sin(x as f64) as float
 }
 #[inline(always)]
 pub fn cos(x: float) -> float {
-    unsafe { f64::cos(x as f64) as float }
+    f64::cos(x as f64) as float
 }
 #[inline(always)]
 pub fn tan(x: float) -> float {
-    unsafe { f64::tan(x as f64) as float }
+    f64::tan(x as f64) as float
 }
 
 #[cfg(notest)]
diff --git a/src/libcore/path.rs b/src/libcore/path.rs
index 7de0f355dd2..0e8dbd144b1 100644
--- a/src/libcore/path.rs
+++ b/src/libcore/path.rs
@@ -389,13 +389,11 @@ impl GenericPath for PosixPath {
     }
 
     fn dirname(&self) -> ~str {
-        unsafe {
-            let s = self.dir_path().to_str();
-            if s.len() == 0 {
-                ~"."
-            } else {
-                s
-            }
+        let s = self.dir_path().to_str();
+        if s.len() == 0 {
+            ~"."
+        } else {
+            s
         }
     }
 
@@ -439,10 +437,8 @@ impl GenericPath for PosixPath {
     }
 
     fn with_filename(&self, f: &str) -> PosixPath {
-        unsafe {
-            assert!(! str::any(f, |c| windows::is_sep(c as u8)));
-            self.dir_path().push(f)
-        }
+        assert!(! str::any(f, |c| windows::is_sep(c as u8)));
+        self.dir_path().push(f)
     }
 
     fn with_filestem(&self, s: &str) -> PosixPath {
@@ -509,7 +505,7 @@ impl GenericPath for PosixPath {
             for str::each_split_nonempty(*e, |c| windows::is_sep(c as u8)) |s| {
                 ss.push(s.to_owned())
             }
-            unsafe { v.push_all_move(ss); }
+            v.push_all_move(ss);
         }
         PosixPath { is_absolute: self.is_absolute,
                     components: v }
@@ -521,14 +517,14 @@ impl GenericPath for PosixPath {
         for str::each_split_nonempty(s, |c| windows::is_sep(c as u8)) |s| {
             ss.push(s.to_owned())
         }
-        unsafe { v.push_all_move(ss); }
+        v.push_all_move(ss);
         PosixPath { components: v, ..copy *self }
     }
 
     fn pop(&self) -> PosixPath {
         let mut cs = copy self.components;
         if cs.len() != 0 {
-            unsafe { cs.pop(); }
+            cs.pop();
         }
         return PosixPath {
             is_absolute: self.is_absolute,
@@ -607,13 +603,11 @@ impl GenericPath for WindowsPath {
     }
 
     fn dirname(&self) -> ~str {
-        unsafe {
-            let s = self.dir_path().to_str();
-            if s.len() == 0 {
-                ~"."
-            } else {
-                s
-            }
+        let s = self.dir_path().to_str();
+        if s.len() == 0 {
+            ~"."
+        } else {
+            s
         }
     }
 
@@ -770,7 +764,7 @@ impl GenericPath for WindowsPath {
             for str::each_split_nonempty(*e, |c| windows::is_sep(c as u8)) |s| {
                 ss.push(s.to_owned())
             }
-            unsafe { v.push_all_move(ss); }
+            v.push_all_move(ss);
         }
         // tedious, but as-is, we can't use ..self
         return WindowsPath {
@@ -787,14 +781,14 @@ impl GenericPath for WindowsPath {
         for str::each_split_nonempty(s, |c| windows::is_sep(c as u8)) |s| {
             ss.push(s.to_owned())
         }
-        unsafe { v.push_all_move(ss); }
+        v.push_all_move(ss);
         return WindowsPath { components: v, ..copy *self }
     }
 
     fn pop(&self) -> WindowsPath {
         let mut cs = copy self.components;
         if cs.len() != 0 {
-            unsafe { cs.pop(); }
+            cs.pop();
         }
         return WindowsPath {
             host: copy self.host,
@@ -820,18 +814,14 @@ impl GenericPath for WindowsPath {
 
 pub fn normalize(components: &[~str]) -> ~[~str] {
     let mut cs = ~[];
-    unsafe {
-        for components.each |c| {
-            unsafe {
-                if *c == ~"." && components.len() > 1 { loop; }
-                if *c == ~"" { loop; }
-                if *c == ~".." && cs.len() != 0 {
-                    cs.pop();
-                    loop;
-                }
-                cs.push(copy *c);
-            }
+    for components.each |c| {
+        if *c == ~"." && components.len() > 1 { loop; }
+        if *c == ~"" { loop; }
+        if *c == ~".." && cs.len() != 0 {
+            cs.pop();
+            loop;
         }
+        cs.push(copy *c);
     }
     cs
 }
diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs
index 70bdb6f41d8..14e17de4fbd 100644
--- a/src/libcore/ptr.rs
+++ b/src/libcore/ptr.rs
@@ -55,17 +55,13 @@ pub fn addr_of<T>(val: &T) -> *T { unsafe { rusti::addr_of(*val) } }
 /// Calculate the offset from a pointer
 #[inline(always)]
 pub fn offset<T>(ptr: *T, count: uint) -> *T {
-    unsafe {
-        (ptr as uint + count * sys::size_of::<T>()) as *T
-    }
+    (ptr as uint + count * sys::size_of::<T>()) as *T
 }
 
 /// Calculate the offset from a const pointer
 #[inline(always)]
 pub fn const_offset<T>(ptr: *const T, count: uint) -> *const T {
-    unsafe {
-        (ptr as uint + count * sys::size_of::<T>()) as *T
-    }
+    (ptr as uint + count * sys::size_of::<T>()) as *T
 }
 
 /// Calculate the offset from a mut pointer
diff --git a/src/libcore/rt/context.rs b/src/libcore/rt/context.rs
index 9dc9f5da8c1..4714be9e3d5 100644
--- a/src/libcore/rt/context.rs
+++ b/src/libcore/rt/context.rs
@@ -205,8 +205,6 @@ fn align_down(sp: *mut uint) -> *mut uint {
 #[inline(always)]
 pub fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T {
     use core::sys::size_of;
-    unsafe {
-        (ptr as int + count * (size_of::<T>() as int)) as *mut T
-    }
+    (ptr as int + count * (size_of::<T>() as int)) as *mut T
 }
 
diff --git a/src/libcore/rt/thread_local_storage.rs b/src/libcore/rt/thread_local_storage.rs
index c8a9689bbc6..366996fb935 100644
--- a/src/libcore/rt/thread_local_storage.rs
+++ b/src/libcore/rt/thread_local_storage.rs
@@ -21,17 +21,17 @@ pub type Key = pthread_key_t;
 
 #[cfg(unix)]
 pub unsafe fn create(key: &mut Key) {
-    unsafe { assert!(0 == pthread_key_create(key, null())); }
+    assert!(0 == pthread_key_create(key, null()));
 }
 
 #[cfg(unix)]
 pub unsafe fn set(key: Key, value: *mut c_void) {
-    unsafe { assert!(0 == pthread_setspecific(key, value)); }
+    assert!(0 == pthread_setspecific(key, value));
 }
 
 #[cfg(unix)]
 pub unsafe fn get(key: Key) -> *mut c_void {
-    unsafe { pthread_getspecific(key) }
+    pthread_getspecific(key)
 }
 
 #[cfg(target_os="macos")]
diff --git a/src/libcore/run.rs b/src/libcore/run.rs
index f6f4b9a397d..49df2938afd 100644
--- a/src/libcore/run.rs
+++ b/src/libcore/run.rs
@@ -382,64 +382,62 @@ pub struct ProgramOutput {status: int, out: ~str, err: ~str}
  * the contents of stdout and the contents of stderr.
  */
 pub fn program_output(prog: &str, args: &[~str]) -> ProgramOutput {
-    unsafe {
-        let pipe_in = os::pipe();
-        let pipe_out = os::pipe();
-        let pipe_err = os::pipe();
-        let pid = spawn_process(prog, args, &None, &None,
-                                pipe_in.in, pipe_out.out, pipe_err.out);
-
-        os::close(pipe_in.in);
-        os::close(pipe_out.out);
-        os::close(pipe_err.out);
-        if pid == -1i32 {
-            os::close(pipe_in.out);
-            os::close(pipe_out.in);
-            os::close(pipe_err.in);
-            fail!();
-        }
+    let pipe_in = os::pipe();
+    let pipe_out = os::pipe();
+    let pipe_err = os::pipe();
+    let pid = spawn_process(prog, args, &None, &None,
+                            pipe_in.in, pipe_out.out, pipe_err.out);
 
+    os::close(pipe_in.in);
+    os::close(pipe_out.out);
+    os::close(pipe_err.out);
+    if pid == -1i32 {
         os::close(pipe_in.out);
+        os::close(pipe_out.in);
+        os::close(pipe_err.in);
+        fail!();
+    }
 
-        // Spawn two entire schedulers to read both stdout and sterr
-        // in parallel so we don't deadlock while blocking on one
-        // or the other. FIXME (#2625): Surely there's a much more
-        // clever way to do this.
-        let (p, ch) = stream();
-        let ch = SharedChan(ch);
-        let ch_clone = ch.clone();
-        do task::spawn_sched(task::SingleThreaded) {
-            let errput = readclose(pipe_err.in);
-            ch.send((2, errput));
-        };
-        do task::spawn_sched(task::SingleThreaded) {
-            let output = readclose(pipe_out.in);
-            ch_clone.send((1, output));
-        };
-        let status = run::waitpid(pid);
-        let mut errs = ~"";
-        let mut outs = ~"";
-        let mut count = 2;
-        while count > 0 {
-            let stream = p.recv();
-            match stream {
-                (1, copy s) => {
-                    outs = s;
-                }
-                (2, copy s) => {
-                    errs = s;
-                }
-                (n, _) => {
-                    fail!(fmt!("program_output received an unexpected file \
-                               number: %u", n));
-                }
-            };
-            count -= 1;
+    os::close(pipe_in.out);
+
+    // Spawn two entire schedulers to read both stdout and sterr
+    // in parallel so we don't deadlock while blocking on one
+    // or the other. FIXME (#2625): Surely there's a much more
+    // clever way to do this.
+    let (p, ch) = stream();
+    let ch = SharedChan(ch);
+    let ch_clone = ch.clone();
+    do task::spawn_sched(task::SingleThreaded) {
+        let errput = readclose(pipe_err.in);
+        ch.send((2, errput));
+    };
+    do task::spawn_sched(task::SingleThreaded) {
+        let output = readclose(pipe_out.in);
+        ch_clone.send((1, output));
+    };
+    let status = run::waitpid(pid);
+    let mut errs = ~"";
+    let mut outs = ~"";
+    let mut count = 2;
+    while count > 0 {
+        let stream = p.recv();
+        match stream {
+            (1, copy s) => {
+                outs = s;
+            }
+            (2, copy s) => {
+                errs = s;
+            }
+            (n, _) => {
+                fail!(fmt!("program_output received an unexpected file \
+                           number: %u", n));
+            }
         };
-        return ProgramOutput {status: status,
-                              out: outs,
-                              err: errs};
-    }
+        count -= 1;
+    };
+    return ProgramOutput {status: status,
+                          out: outs,
+                          err: errs};
 }
 
 pub fn writeclose(fd: c_int, s: ~str) {
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 521c8266e05..b0653db365e 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -170,18 +170,16 @@ pub fn push_char(s: &mut ~str, ch: char) {
 /// Convert a char to a string
 pub fn from_char(ch: char) -> ~str {
     let mut buf = ~"";
-    unsafe { push_char(&mut buf, ch); }
+    push_char(&mut buf, ch);
     buf
 }
 
 /// Convert a vector of chars to a string
 pub fn from_chars(chs: &[char]) -> ~str {
     let mut buf = ~"";
-    unsafe {
-        reserve(&mut buf, chs.len());
-        for vec::each(chs) |ch| {
-            push_char(&mut buf, *ch);
-        }
+    reserve(&mut buf, chs.len());
+    for vec::each(chs) |ch| {
+        push_char(&mut buf, *ch);
     }
     buf
 }
@@ -226,9 +224,7 @@ pub fn push_str(lhs: &mut ~str, rhs: &str) {
 #[inline(always)]
 pub fn append(lhs: ~str, rhs: &str) -> ~str {
     let mut v = lhs;
-    unsafe {
-        push_str_no_overallocate(&mut v, rhs);
-    }
+    push_str_no_overallocate(&mut v, rhs);
     v
 }
 
@@ -236,7 +232,7 @@ pub fn append(lhs: ~str, rhs: &str) -> ~str {
 pub fn concat(v: &[~str]) -> ~str {
     let mut s: ~str = ~"";
     for vec::each(v) |ss| {
-        unsafe { push_str(&mut s, *ss) };
+        push_str(&mut s, *ss);
     }
     s
 }
@@ -245,8 +241,8 @@ pub fn concat(v: &[~str]) -> ~str {
 pub fn connect(v: &[~str], sep: &str) -> ~str {
     let mut s = ~"", first = true;
     for vec::each(v) |ss| {
-        if first { first = false; } else { unsafe { push_str(&mut s, sep); } }
-        unsafe { push_str(&mut s, *ss) };
+        if first { first = false; } else { push_str(&mut s, sep); }
+        push_str(&mut s, *ss);
     }
     s
 }
@@ -255,8 +251,8 @@ pub fn connect(v: &[~str], sep: &str) -> ~str {
 pub fn connect_slices(v: &[&str], sep: &str) -> ~str {
     let mut s = ~"", first = true;
     for vec::each(v) |ss| {
-        if first { first = false; } else { unsafe { push_str(&mut s, sep); } }
-        unsafe { push_str(&mut s, *ss) };
+        if first { first = false; } else { push_str(&mut s, sep); }
+        push_str(&mut s, *ss);
     }
     s
 }
@@ -2251,16 +2247,14 @@ pub mod raw {
             assert!((end <= n));
 
             let mut v = vec::with_capacity(end - begin + 1u);
-            unsafe {
-                do vec::as_imm_buf(v) |vbuf, _vlen| {
-                    let vbuf = ::cast::transmute_mut_unsafe(vbuf);
-                    let src = ptr::offset(sbuf, begin);
-                    ptr::copy_memory(vbuf, src, end - begin);
-                }
-                vec::raw::set_len(&mut v, end - begin);
-                v.push(0u8);
-                ::cast::transmute(v)
+            do vec::as_imm_buf(v) |vbuf, _vlen| {
+                let vbuf = ::cast::transmute_mut_unsafe(vbuf);
+                let src = ptr::offset(sbuf, begin);
+                ptr::copy_memory(vbuf, src, end - begin);
             }
+            vec::raw::set_len(&mut v, end - begin);
+            v.push(0u8);
+            ::cast::transmute(v)
         }
     }
 
@@ -2304,7 +2298,7 @@ pub mod raw {
     }
 
     /// Removes the last byte from a string and returns it. (Not UTF-8 safe).
-    pub unsafe fn pop_byte(s: &mut ~str) -> u8 {
+    pub fn pop_byte(s: &mut ~str) -> u8 {
         let len = len(*s);
         assert!((len > 0u));
         let b = s[len - 1u];
@@ -2313,7 +2307,7 @@ pub mod raw {
     }
 
     /// Removes the first byte from a string and returns it. (Not UTF-8 safe).
-    pub unsafe fn shift_byte(s: &mut ~str) -> u8 {
+    pub fn shift_byte(s: &mut ~str) -> u8 {
         let len = len(*s);
         assert!((len > 0u));
         let b = s[0];
diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs
index 52ca0204126..678005ce06f 100644
--- a/src/libcore/sys.rs
+++ b/src/libcore/sys.rs
@@ -127,10 +127,8 @@ pub fn refcount<T>(t: @T) -> uint {
 }
 
 pub fn log_str<T>(t: &T) -> ~str {
-    unsafe {
-        do io::with_str_writer |wr| {
-            repr::write_repr(wr, t)
-        }
+    do io::with_str_writer |wr| {
+        repr::write_repr(wr, t)
     }
 }
 
@@ -157,10 +155,8 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
 }
 
 pub fn fail_assert(msg: &str, file: &str, line: uint) -> ! {
-    unsafe {
-        let (msg, file) = (msg.to_owned(), file.to_owned());
-        begin_unwind(~"assertion failed: " + msg, file, line)
-    }
+    let (msg, file) = (msg.to_owned(), file.to_owned());
+    begin_unwind(~"assertion failed: " + msg, file, line)
 }
 
 #[cfg(test)]
diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs
index e4ee430cdda..d712bf8f98f 100644
--- a/src/libcore/task/mod.rs
+++ b/src/libcore/task/mod.rs
@@ -39,10 +39,9 @@ use result::Result;
 use comm::{stream, Chan, GenericChan, GenericPort, Port};
 use prelude::*;
 use result;
-use task::rt::{task_id, sched_id};
+use task::rt::{task_id, sched_id, rust_task};
 use util;
 use util::replace;
-use unstable::finally::Finally;
 
 #[cfg(test)] use comm::SharedChan;
 
@@ -566,28 +565,48 @@ pub fn get_scheduler() -> Scheduler {
  * ~~~
  */
 pub unsafe fn unkillable<U>(f: &fn() -> U) -> U {
-    unsafe {
-        let t = rt::rust_get_task();
-        rt::rust_task_inhibit_kill(t);
-        do (|| {
-            f()
-        }).finally {
-            rt::rust_task_allow_kill(t);
+    struct AllowFailure {
+        t: *rust_task,
+        drop {
+            unsafe {
+                rt::rust_task_allow_kill(self.t);
+            }
+        }
+    }
+
+    fn AllowFailure(t: *rust_task) -> AllowFailure{
+        AllowFailure {
+            t: t
         }
     }
+
+    let t = rt::rust_get_task();
+    let _allow_failure = AllowFailure(t);
+    rt::rust_task_inhibit_kill(t);
+    f()
 }
 
 /// The inverse of unkillable. Only ever to be used nested in unkillable().
 pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
-    unsafe {
-        let t = rt::rust_get_task();
-        rt::rust_task_allow_kill(t);
-        do (|| {
-            f()
-        }).finally {
-            rt::rust_task_inhibit_kill(t);
+    struct DisallowFailure {
+        t: *rust_task,
+        drop {
+            unsafe {
+                rt::rust_task_inhibit_kill(self.t);
+            }
         }
     }
+
+    fn DisallowFailure(t: *rust_task) -> DisallowFailure {
+        DisallowFailure {
+            t: t
+        }
+    }
+
+    let t = rt::rust_get_task();
+    let _allow_failure = DisallowFailure(t);
+    rt::rust_task_allow_kill(t);
+    f()
 }
 
 /**
@@ -595,17 +614,27 @@ pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
  * For use with exclusive ARCs, which use pthread mutexes directly.
  */
 pub unsafe fn atomically<U>(f: &fn() -> U) -> U {
-    unsafe {
-        let t = rt::rust_get_task();
-        rt::rust_task_inhibit_kill(t);
-        rt::rust_task_inhibit_yield(t);
-        do (|| {
-            f()
-        }).finally {
-            rt::rust_task_allow_yield(t);
-            rt::rust_task_allow_kill(t);
+    struct DeferInterrupts {
+        t: *rust_task,
+        drop {
+            unsafe {
+                rt::rust_task_allow_yield(self.t);
+                rt::rust_task_allow_kill(self.t);
+            }
         }
     }
+
+    fn DeferInterrupts(t: *rust_task) -> DeferInterrupts {
+        DeferInterrupts {
+            t: t
+        }
+    }
+
+    let t = rt::rust_get_task();
+    let _interrupts = DeferInterrupts(t);
+    rt::rust_task_inhibit_kill(t);
+    rt::rust_task_inhibit_yield(t);
+    f()
 }
 
 #[test] #[should_fail] #[ignore(cfg(windows))]
diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs
index e1b645cd562..c71f7d26d40 100644
--- a/src/libcore/task/spawn.rs
+++ b/src/libcore/task/spawn.rs
@@ -157,13 +157,13 @@ struct AncestorList(Option<unstable::Exclusive<AncestorNode>>);
 // Accessors for taskgroup arcs and ancestor arcs that wrap the unsafety.
 #[inline(always)]
 fn access_group<U>(x: &TaskGroupArc, blk: &fn(TaskGroupInner) -> U) -> U {
-    unsafe { x.with(blk) }
+    x.with(blk)
 }
 
 #[inline(always)]
 fn access_ancestors<U>(x: &unstable::Exclusive<AncestorNode>,
                        blk: &fn(x: &mut AncestorNode) -> U) -> U {
-    unsafe { x.with(blk) }
+    x.with(blk)
 }
 
 // Iterates over an ancestor list.
diff --git a/src/libcore/unstable.rs b/src/libcore/unstable.rs
index 9ccce0cfe76..e43d321dc4d 100644
--- a/src/libcore/unstable.rs
+++ b/src/libcore/unstable.rs
@@ -152,45 +152,37 @@ pub type SharedMutableState<T> = ArcDestruct<T>;
 pub unsafe fn shared_mutable_state<T:Owned>(data: T) ->
         SharedMutableState<T> {
     let data = ~ArcData { count: 1, data: Some(data) };
-    unsafe {
-        let ptr = cast::transmute(data);
-        ArcDestruct(ptr)
-    }
+    let ptr = cast::transmute(data);
+    ArcDestruct(ptr)
 }
 
 #[inline(always)]
 pub unsafe fn get_shared_mutable_state<T:Owned>(
     rc: *SharedMutableState<T>) -> *mut T
 {
-    unsafe {
-        let ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
-        assert!(ptr.count > 0);
-        let r = cast::transmute(ptr.data.get_ref());
-        cast::forget(ptr);
-        return r;
-    }
+    let ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
+    assert!(ptr.count > 0);
+    let r = cast::transmute(ptr.data.get_ref());
+    cast::forget(ptr);
+    return r;
 }
 #[inline(always)]
 pub unsafe fn get_shared_immutable_state<'a,T:Owned>(
         rc: &'a SharedMutableState<T>) -> &'a T {
-    unsafe {
-        let ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
-        assert!(ptr.count > 0);
-        // Cast us back into the correct region
-        let r = cast::transmute_region(ptr.data.get_ref());
-        cast::forget(ptr);
-        return r;
-    }
+    let ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
+    assert!(ptr.count > 0);
+    // Cast us back into the correct region
+    let r = cast::transmute_region(ptr.data.get_ref());
+    cast::forget(ptr);
+    return r;
 }
 
 pub unsafe fn clone_shared_mutable_state<T:Owned>(rc: &SharedMutableState<T>)
         -> SharedMutableState<T> {
-    unsafe {
-        let mut ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
-        let new_count = intrinsics::atomic_xadd(&mut ptr.count, 1) + 1;
-        assert!(new_count >= 2);
-        cast::forget(ptr);
-    }
+    let mut ptr: ~ArcData<T> = cast::reinterpret_cast(&(*rc).data);
+    let new_count = intrinsics::atomic_xadd(&mut ptr.count, 1) + 1;
+    assert!(new_count >= 2);
+    cast::forget(ptr);
     ArcDestruct((*rc).data)
 }
 
diff --git a/src/libcore/unstable/exchange_alloc.rs b/src/libcore/unstable/exchange_alloc.rs
index fdf99e9dffe..8ca5486d929 100644
--- a/src/libcore/unstable/exchange_alloc.rs
+++ b/src/libcore/unstable/exchange_alloc.rs
@@ -19,27 +19,25 @@ use ptr::null;
 use intrinsic::TyDesc;
 
 pub unsafe fn malloc(td: *TypeDesc, size: uint) -> *c_void {
-    unsafe {
-        assert!(td.is_not_null());
+    assert!(td.is_not_null());
 
-        let total_size = get_box_size(size, (*td).align);
-        let p = c_malloc(total_size as size_t);
-        assert!(p.is_not_null());
+    let total_size = get_box_size(size, (*td).align);
+    let p = c_malloc(total_size as size_t);
+    assert!(p.is_not_null());
 
-        // FIXME #3475: Converting between our two different tydesc types
-        let td: *TyDesc = transmute(td);
+    // FIXME #3475: Converting between our two different tydesc types
+    let td: *TyDesc = transmute(td);
 
-        let box: &mut BoxRepr = transmute(p);
-        box.header.ref_count = -1; // Exchange values not ref counted
-        box.header.type_desc = td;
-        box.header.prev = null();
-        box.header.next = null();
+    let box: &mut BoxRepr = transmute(p);
+    box.header.ref_count = -1; // Exchange values not ref counted
+    box.header.type_desc = td;
+    box.header.prev = null();
+    box.header.next = null();
 
-        let exchange_count = &mut *rust_get_exchange_count_ptr();
-        atomic_xadd(exchange_count, 1);
+    let exchange_count = &mut *rust_get_exchange_count_ptr();
+    atomic_xadd(exchange_count, 1);
 
-        return transmute(box);
-    }
+    return transmute(box);
 }
 /**
 Thin wrapper around libc::malloc, none of the box header
diff --git a/src/libcore/unstable/extfmt.rs b/src/libcore/unstable/extfmt.rs
index be2aecf0c2a..ad3dce0a749 100644
--- a/src/libcore/unstable/extfmt.rs
+++ b/src/libcore/unstable/extfmt.rs
@@ -512,7 +512,7 @@ pub mod rt {
                 None
             }
         } else { Some('-') };
-        unsafe { pad(cv, s, head, PadSigned, buf) };
+        pad(cv, s, head, PadSigned, buf);
     }
     pub fn conv_uint(cv: Conv, u: uint, buf: &mut ~str) {
         let prec = get_int_precision(cv);
@@ -524,7 +524,7 @@ pub mod rt {
               TyBits => uint_to_str_prec(u, 2, prec),
               TyOctal => uint_to_str_prec(u, 8, prec)
             };
-        unsafe { pad(cv, rs, None, PadUnsigned, buf) };
+        pad(cv, rs, None, PadUnsigned, buf);
     }
     pub fn conv_bool(cv: Conv, b: bool, buf: &mut ~str) {
         let s = if b { "true" } else { "false" };
@@ -533,7 +533,7 @@ pub mod rt {
         conv_str(cv, s, buf);
     }
     pub fn conv_char(cv: Conv, c: char, buf: &mut ~str) {
-        unsafe { pad(cv, "", Some(c), PadNozero, buf) };
+        pad(cv, "", Some(c), PadNozero, buf);
     }
     pub fn conv_str(cv: Conv, s: &str, buf: &mut ~str) {
         // For strings, precision is the maximum characters
@@ -546,14 +546,14 @@ pub mod rt {
             s
           }
         };
-        unsafe { pad(cv, unpadded, None, PadNozero, buf) };
+        pad(cv, unpadded, None, PadNozero, buf);
     }
     pub fn conv_float(cv: Conv, f: float, buf: &mut ~str) {
         let (to_str, digits) = match cv.precision {
               CountIs(c) => (float::to_str_exact, c as uint),
               CountImplied => (float::to_str_digits, 6u)
         };
-        let mut s = unsafe { to_str(f, digits) };
+        let mut s = to_str(f, digits);
         let head = if 0.0 <= f {
             if have_flag(cv.flags, flag_sign_always) {
                 Some('+')
@@ -563,7 +563,7 @@ pub mod rt {
                 None
             }
         } else { None };
-        unsafe { pad(cv, s, head, PadFloat, buf) };
+        pad(cv, s, head, PadFloat, buf);
     }
     pub fn conv_poly<T>(cv: Conv, v: &T, buf: &mut ~str) {
         let s = sys::log_str(v);
diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs
index dad990c0f97..be776a39742 100644
--- a/src/libcore/unstable/lang.rs
+++ b/src/libcore/unstable/lang.rs
@@ -44,7 +44,7 @@ pub fn fail_(expr: *c_char, file: *c_char, line: size_t) -> ! {
 }
 
 #[lang="fail_bounds_check"]
-pub unsafe fn fail_bounds_check(file: *c_char, line: size_t,
+pub fn fail_bounds_check(file: *c_char, line: size_t,
                                 index: size_t, len: size_t) {
     let msg = fmt!("index out of bounds: the len is %d but the index is %d",
                     len as int, index as int);
@@ -53,7 +53,7 @@ pub unsafe fn fail_bounds_check(file: *c_char, line: size_t,
     }
 }
 
-pub unsafe fn fail_borrowed() {
+pub fn fail_borrowed() {
     let msg = "borrowed";
     do str::as_buf(msg) |msg_p, _| {
         do str::as_buf("???") |file_p, _| {
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index fae56aac38f..1c995662808 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -615,9 +615,7 @@ pub fn build_link_meta(sess: Session, c: &ast::crate, output: &Path,
 }
 
 pub fn truncated_hash_result(symbol_hasher: &hash::State) -> ~str {
-    unsafe {
-        symbol_hasher.result_str()
-    }
+    symbol_hasher.result_str()
 }
 
 
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index 721bb996262..116b70bf7e3 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -33,6 +33,7 @@ use util::ppaux::ty_to_str;
 
 use core::hashmap::HashSet;
 use core::uint;
+use core::util::with;
 use syntax::ast::m_mutbl;
 use syntax::ast;
 use syntax::ast_util;
@@ -40,13 +41,18 @@ use syntax::codemap::span;
 use syntax::print::pprust;
 use syntax::visit;
 
+struct PurityState {
+    def: ast::node_id,
+    purity: ast::purity
+}
+
 struct CheckLoanCtxt {
     bccx: @BorrowckCtxt,
     req_maps: ReqMaps,
 
     reported: HashSet<ast::node_id>,
 
-    declared_purity: @mut ast::purity,
+    declared_purity: @mut PurityState,
     fn_args: @mut @~[ast::node_id]
 }
 
@@ -62,6 +68,16 @@ enum purity_cause {
     pc_cmt(bckerr)
 }
 
+// if we're not pure, why?
+#[deriving(Eq)]
+enum impurity_cause {
+    // some surrounding block was marked as 'unsafe'
+    pc_unsafe,
+
+    // nothing was unsafe, and nothing was pure
+    pc_default,
+}
+
 pub fn check_loans(bccx: @BorrowckCtxt,
                    +req_maps: ReqMaps,
                    crate: @ast::crate) {
@@ -69,7 +85,8 @@ pub fn check_loans(bccx: @BorrowckCtxt,
         bccx: bccx,
         req_maps: req_maps,
         reported: HashSet::new(),
-        declared_purity: @mut ast::impure_fn,
+        declared_purity: @mut PurityState { purity: ast::impure_fn,
+                                            def: 0 },
         fn_args: @mut @~[]
     };
     let vt = visit::mk_vt(@visit::Visitor {visit_expr: check_loans_in_expr,
@@ -106,16 +123,18 @@ pub impl assignment_type {
 pub impl CheckLoanCtxt {
     fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
 
-    fn purity(&mut self, scope_id: ast::node_id) -> Option<purity_cause> {
-        let default_purity = match *self.declared_purity {
+    fn purity(&mut self, scope_id: ast::node_id)
+                -> Either<purity_cause, impurity_cause>
+    {
+        let default_purity = match self.declared_purity.purity {
           // an unsafe declaration overrides all
-          ast::unsafe_fn => return None,
+          ast::unsafe_fn => return Right(pc_unsafe),
 
           // otherwise, remember what was declared as the
           // default, but we must scan for requirements
           // imposed by the borrow check
-          ast::pure_fn => Some(pc_pure_fn),
-          ast::extern_fn | ast::impure_fn => None
+          ast::pure_fn => Left(pc_pure_fn),
+          ast::extern_fn | ast::impure_fn => Right(pc_default)
         };
 
         // scan to see if this scope or any enclosing scope requires
@@ -125,7 +144,7 @@ pub impl CheckLoanCtxt {
         loop {
             match self.req_maps.pure_map.find(&scope_id) {
               None => (),
-              Some(e) => return Some(pc_cmt(*e))
+              Some(e) => return Left(pc_cmt(*e))
             }
 
             match self.tcx().region_maps.opt_encl_scope(scope_id) {
@@ -171,7 +190,7 @@ pub impl CheckLoanCtxt {
     // overloaded operators the callee has an id but no expr.
     // annoying.
     fn check_pure_callee_or_arg(&mut self,
-                                pc: purity_cause,
+                                pc: Either<purity_cause, impurity_cause>,
                                 opt_expr: Option<@ast::expr>,
                                 callee_id: ast::node_id,
                                 callee_span: span) {
@@ -196,7 +215,7 @@ pub impl CheckLoanCtxt {
         match opt_expr {
           Some(expr) => {
             match expr.node {
-              ast::expr_path(_) if pc == pc_pure_fn => {
+              ast::expr_path(_) if pc == Left(pc_pure_fn) => {
                 let def = *self.tcx().def_map.get(&expr.id);
                 let did = ast_util::def_id_of_def(def);
                 let is_fn_arg =
@@ -361,10 +380,10 @@ pub impl CheckLoanCtxt {
         // if this is a pure function, only loan-able state can be
         // assigned, because it is uniquely tied to this function and
         // is not visible from the outside
-        match self.purity(ex.id) {
-          None => (),
-          Some(pc_cmt(_)) => {
-            let purity = self.purity(ex.id).get();
+        let purity = self.purity(ex.id);
+        match purity {
+          Right(_) => (),
+          Left(pc_cmt(_)) => {
             // Subtle: Issue #3162.  If we are enforcing purity
             // because there is a reference to aliasable, mutable data
             // that we require to be immutable, we can't allow writes
@@ -376,10 +395,10 @@ pub impl CheckLoanCtxt {
                 ex.span,
                 at.ing_form(self.bccx.cmt_to_str(cmt)));
           }
-          Some(pc_pure_fn) => {
+          Left(pc_pure_fn) => {
             if cmt.lp.is_none() {
                 self.report_purity_error(
-                    pc_pure_fn, ex.span,
+                    purity, ex.span,
                     at.ing_form(self.bccx.cmt_to_str(cmt)));
             }
           }
@@ -462,14 +481,23 @@ pub impl CheckLoanCtxt {
         }
     }
 
-    fn report_purity_error(&mut self, pc: purity_cause, sp: span, msg: ~str) {
+    fn report_purity_error(&mut self, pc: Either<purity_cause, impurity_cause>,
+                           sp: span, msg: ~str) {
         match pc {
-          pc_pure_fn => {
+          Right(pc_default) => { fail!(~"pc_default should be filtered sooner") }
+          Right(pc_unsafe) => {
+            // this error was prevented by being marked as unsafe, so flag the
+            // definition as having contributed to the validity of the program
+            let def = self.declared_purity.def;
+            debug!("flagging %? as a used unsafe source", def);
+            self.tcx().used_unsafe.insert(def);
+          }
+          Left(pc_pure_fn) => {
             self.tcx().sess.span_err(
                 sp,
                 fmt!("%s prohibited in pure context", msg));
           }
-          pc_cmt(ref e) => {
+          Left(pc_cmt(ref e)) => {
             if self.reported.insert((*e).cmt.id) {
                 self.tcx().sess.span_err(
                     (*e).cmt.span,
@@ -556,16 +584,32 @@ pub impl CheckLoanCtxt {
                   callee_id: ast::node_id,
                   callee_span: span,
                   args: &[@ast::expr]) {
-        match self.purity(expr.id) {
-          None => {}
-          Some(ref pc) => {
-            self.check_pure_callee_or_arg(
-                (*pc), callee, callee_id, callee_span);
-            for args.each |arg| {
-                self.check_pure_callee_or_arg(
-                    (*pc), Some(*arg), arg.id, arg.span);
+        let pc = self.purity(expr.id);
+        match pc {
+            // no purity, no need to check for anything
+            Right(pc_default) => return,
+
+            // some form of purity, definitely need to check
+            Left(_) => (),
+
+            // Unsafe trumped. To see if the unsafe is necessary, see what the
+            // purity would have been without a trump, and if it's some form
+            // of purity then we need to go ahead with the check
+            Right(pc_unsafe) => {
+                match do with(&mut self.declared_purity.purity,
+                              ast::impure_fn) { self.purity(expr.id) } {
+                    Right(pc_unsafe) => fail!(~"unsafe can't trump twice"),
+                    Right(pc_default) => return,
+                    Left(_) => ()
+                }
             }
-          }
+
+        }
+        self.check_pure_callee_or_arg(
+            pc, callee, callee_id, callee_span);
+        for args.each |arg| {
+            self.check_pure_callee_or_arg(
+                pc, Some(*arg), arg.id, arg.span);
         }
     }
 }
@@ -580,27 +624,32 @@ fn check_loans_in_fn(fk: &visit::fn_kind,
     let is_stack_closure = self.is_stack_closure(id);
     let fty = ty::node_id_to_type(self.tcx(), id);
 
-    let declared_purity;
+    let declared_purity, src;
     match *fk {
         visit::fk_item_fn(*) | visit::fk_method(*) |
         visit::fk_dtor(*) => {
             declared_purity = ty::ty_fn_purity(fty);
+            src = id;
         }
 
         visit::fk_anon(*) | visit::fk_fn_block(*) => {
             let fty_sigil = ty::ty_closure_sigil(fty);
             check_moves_from_captured_variables(self, id, fty_sigil);
-            declared_purity = ty::determine_inherited_purity(
-                *self.declared_purity,
-                ty::ty_fn_purity(fty),
+            let pair = ty::determine_inherited_purity(
+                (self.declared_purity.purity, self.declared_purity.def),
+                (ty::ty_fn_purity(fty), id),
                 fty_sigil);
+            declared_purity = pair.first();
+            src = pair.second();
         }
     }
 
     debug!("purity on entry=%?", copy self.declared_purity);
     do save_and_restore_managed(self.declared_purity) {
         do save_and_restore_managed(self.fn_args) {
-            *self.declared_purity = declared_purity;
+            self.declared_purity = @mut PurityState {
+                purity: declared_purity, def: src
+            };
 
             match *fk {
                 visit::fk_anon(*) |
@@ -754,7 +803,10 @@ fn check_loans_in_block(blk: &ast::blk,
           ast::default_blk => {
           }
           ast::unsafe_blk => {
-            *self.declared_purity = ast::unsafe_fn;
+            *self.declared_purity = PurityState {
+                purity: ast::unsafe_fn,
+                def: blk.node.id,
+            };
           }
         }
 
diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs
index 0dd28e5ca7c..bacbeb851aa 100644
--- a/src/librustc/middle/lang_items.rs
+++ b/src/librustc/middle/lang_items.rs
@@ -369,7 +369,7 @@ pub impl<'self> LanguageItemCollector<'self> {
     }
 
     fn collect_local_language_items(&self) {
-        let this = unsafe { ptr::addr_of(&self) };
+        let this = ptr::addr_of(&self);
         visit_crate(*self.crate, (), mk_simple_visitor(@SimpleVisitor {
             visit_item: |item| {
                 for item.attrs.each |attribute| {
diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs
index 6f8992bf1ca..876ed76f987 100644
--- a/src/librustc/middle/lint.rs
+++ b/src/librustc/middle/lint.rs
@@ -75,6 +75,7 @@ pub enum lint {
     default_methods,
     deprecated_mutable_fields,
     deprecated_drop,
+    unused_unsafe,
     foreign_mode,
 
     managed_heap_memory,
@@ -256,6 +257,13 @@ pub fn get_lint_dict() -> LintDict {
             default: deny
         }),
 
+        (~"unused_unsafe",
+         LintSpec {
+            lint: unused_unsafe,
+            desc: "unnecessary use of an \"unsafe\" block or function",
+            default: warn
+        }),
+
         (~"unused_variable",
          LintSpec {
             lint: unused_variable,
@@ -490,6 +498,7 @@ fn check_item(i: @ast::item, cx: ty::ctxt) {
     check_item_default_methods(cx, i);
     check_item_deprecated_mutable_fields(cx, i);
     check_item_deprecated_drop(cx, i);
+    check_item_unused_unsafe(cx, i);
 }
 
 // Take a visitor, and modify it so that it will not proceed past subitems.
@@ -923,19 +932,55 @@ fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
     }
 }
 
+fn check_item_unused_unsafe(cx: ty::ctxt, it: @ast::item) {
+    let visit_expr: @fn(@ast::expr) = |e| {
+        match e.node {
+            ast::expr_block(ref blk) if blk.node.rules == ast::unsafe_blk => {
+                if !cx.used_unsafe.contains(&blk.node.id) {
+                    cx.sess.span_lint(unused_unsafe, blk.node.id, it.id,
+                                      blk.span,
+                                      ~"unnecessary \"unsafe\" block");
+                }
+            }
+            _ => ()
+        }
+    };
+
+    let visit = item_stopping_visitor(
+        visit::mk_simple_visitor(@visit::SimpleVisitor {
+            visit_expr: visit_expr,
+            .. *visit::default_simple_visitor()
+        }));
+    visit::visit_item(it, (), visit);
+}
+
 fn check_fn(tcx: ty::ctxt, fk: &visit::fn_kind, decl: &ast::fn_decl,
             _body: &ast::blk, span: span, id: ast::node_id) {
     debug!("lint check_fn fk=%? id=%?", fk, id);
 
-    // don't complain about blocks, since they tend to get their modes
-    // specified from the outside
+    // Check for an 'unsafe fn' which doesn't need to be unsafe
+    match *fk {
+        visit::fk_item_fn(_, _, ast::unsafe_fn, _) => {
+            if !tcx.used_unsafe.contains(&id) {
+                tcx.sess.span_lint(unused_unsafe, id, id, span,
+                                   ~"unnecessary \"unsafe\" function");
+            }
+        }
+        _ => ()
+    }
+
+    // Check for deprecated modes
     match *fk {
-      visit::fk_fn_block(*) => { return; }
-      _ => {}
+        // don't complain about blocks, since they tend to get their modes
+        // specified from the outside
+        visit::fk_fn_block(*) => {}
+
+        _ => {
+            let fn_ty = ty::node_id_to_type(tcx, id);
+            check_fn_deprecated_modes(tcx, fn_ty, decl, span, id);
+        }
     }
 
-    let fn_ty = ty::node_id_to_type(tcx, id);
-    check_fn_deprecated_modes(tcx, fn_ty, decl, span, id);
 }
 
 fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: &ast::fn_decl,
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index fa902b7bf56..b2225963d2c 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -19,7 +19,7 @@ use metadata::csearch::get_type_name_if_impl;
 use metadata::cstore::find_extern_mod_stmt_cnum;
 use metadata::decoder::{def_like, dl_def, dl_field, dl_impl};
 use middle::lang_items::LanguageItems;
-use middle::lint::{deny, allow, forbid, level, unused_imports, warn};
+use middle::lint::{allow, level, unused_imports};
 use middle::lint::{get_lint_level, get_lint_settings_level};
 use middle::pat_util::pat_bindings;
 
@@ -5212,17 +5212,11 @@ pub impl Resolver {
                     import_resolution.span != dummy_sp() &&
                     import_resolution.privacy != Public {
                 import_resolution.state.warned = true;
-                match self.unused_import_lint_level(module_) {
-                    warn => {
-                        self.session.span_warn(copy import_resolution.span,
-                                               ~"unused import");
-                    }
-                    deny | forbid => {
-                      self.session.span_err(copy import_resolution.span,
-                                            ~"unused import");
-                    }
-                    allow => ()
-                }
+                let span = import_resolution.span;
+                self.session.span_lint_level(
+                    self.unused_import_lint_level(module_),
+                    span,
+                    ~"unused import");
             }
         }
     }
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 7e64a219ecd..f58e066526c 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -91,10 +91,8 @@ pub struct icx_popper {
 #[unsafe_destructor]
 impl Drop for icx_popper {
     fn finalize(&self) {
-        unsafe {
-            if self.ccx.sess.count_llvm_insns() {
-                self.ccx.stats.llvm_insn_ctxt.pop();
-            }
+        if self.ccx.sess.count_llvm_insns() {
+            self.ccx.stats.llvm_insn_ctxt.pop();
         }
     }
 }
@@ -145,9 +143,7 @@ pub fn decl_fn(llmod: ModuleRef, name: &str, cc: lib::llvm::CallConv,
             llvm::LLVMGetOrInsertFunction(llmod, buf, llty)
         }
     });
-    unsafe {
-        lib::llvm::SetFunctionCallConv(llfn, cc);
-    }
+    lib::llvm::SetFunctionCallConv(llfn, cc);
     return llfn;
 }
 
@@ -730,11 +726,9 @@ pub fn cast_shift_expr_rhs(cx: block, op: ast::binop,
 
 pub fn cast_shift_const_rhs(op: ast::binop,
                             lhs: ValueRef, rhs: ValueRef) -> ValueRef {
-    unsafe {
-        cast_shift_rhs(op, lhs, rhs,
-                       |a, b| unsafe { llvm::LLVMConstTrunc(a, b) },
-                       |a, b| unsafe { llvm::LLVMConstZExt(a, b) })
-    }
+    cast_shift_rhs(op, lhs, rhs,
+                   |a, b| unsafe { llvm::LLVMConstTrunc(a, b) },
+                   |a, b| unsafe { llvm::LLVMConstZExt(a, b) })
 }
 
 pub fn cast_shift_rhs(op: ast::binop,
@@ -2865,9 +2859,7 @@ pub fn create_module_map(ccx: @CrateContext) -> ValueRef {
             llvm::LLVMAddGlobal(ccx.llmod, maptype, buf)
         }
     });
-    unsafe {
-        lib::llvm::SetLinkage(map, lib::llvm::InternalLinkage);
-    }
+    lib::llvm::SetLinkage(map, lib::llvm::InternalLinkage);
     let mut elts: ~[ValueRef] = ~[];
     for ccx.module_data.each |key, &val| {
         let elt = C_struct(~[p2i(ccx, C_cstr(ccx, @/*bad*/ copy *key)),
diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs
index fa2be2f415f..d6c045bb115 100644
--- a/src/librustc/middle/trans/build.rs
+++ b/src/librustc/middle/trans/build.rs
@@ -27,9 +27,7 @@ use core::str;
 use core::vec;
 
 pub fn terminate(cx: block, _: &str) {
-    unsafe {
-        cx.terminated = true;
-    }
+    cx.terminated = true;
 }
 
 pub fn check_not_terminated(cx: block) {
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 8c40142335e..c02417aca8b 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -172,17 +172,15 @@ fn cast_safely<T:Copy,U>(val: T) -> U {
 }
 
 fn md_from_metadata<T>(val: debug_metadata) -> T {
-    unsafe {
-        match val {
-          file_metadata(md) => cast_safely(md),
-          compile_unit_metadata(md) => cast_safely(md),
-          subprogram_metadata(md) => cast_safely(md),
-          local_var_metadata(md) => cast_safely(md),
-          tydesc_metadata(md) => cast_safely(md),
-          block_metadata(md) => cast_safely(md),
-          argument_metadata(md) => cast_safely(md),
-          retval_metadata(md) => cast_safely(md)
-        }
+    match val {
+      file_metadata(md) => cast_safely(md),
+      compile_unit_metadata(md) => cast_safely(md),
+      subprogram_metadata(md) => cast_safely(md),
+      local_var_metadata(md) => cast_safely(md),
+      tydesc_metadata(md) => cast_safely(md),
+      block_metadata(md) => cast_safely(md),
+      argument_metadata(md) => cast_safely(md),
+      retval_metadata(md) => cast_safely(md)
     }
 }
 
@@ -190,56 +188,52 @@ fn cached_metadata<T:Copy>(cache: metadata_cache,
                             mdtag: int,
                             eq_fn: &fn(md: T) -> bool)
                          -> Option<T> {
-    unsafe {
-        if cache.contains_key(&mdtag) {
-            let items = cache.get(&mdtag);
-            for items.each |item| {
-                let md: T = md_from_metadata::<T>(*item);
-                if eq_fn(md) {
-                    return option::Some(md);
-                }
+    if cache.contains_key(&mdtag) {
+        let items = cache.get(&mdtag);
+        for items.each |item| {
+            let md: T = md_from_metadata::<T>(*item);
+            if eq_fn(md) {
+                return option::Some(md);
             }
         }
-        return option::None;
     }
+    return option::None;
 }
 
 fn create_compile_unit(cx: @CrateContext) -> @Metadata<CompileUnitMetadata> {
-    unsafe {
-        let cache = get_cache(cx);
-        let crate_name = /*bad*/copy (/*bad*/copy cx.dbg_cx).get().crate_file;
-        let tg = CompileUnitTag;
-        match cached_metadata::<@Metadata<CompileUnitMetadata>>(cache, tg,
-                            |md| md.data.name == crate_name) {
-          option::Some(md) => return md,
-          option::None => ()
-        }
+    let cache = get_cache(cx);
+    let crate_name = /*bad*/copy (/*bad*/copy cx.dbg_cx).get().crate_file;
+    let tg = CompileUnitTag;
+    match cached_metadata::<@Metadata<CompileUnitMetadata>>(cache, tg,
+                        |md| md.data.name == crate_name) {
+      option::Some(md) => return md,
+      option::None => ()
+    }
 
-        let (_, work_dir) = get_file_path_and_dir(
-            cx.sess.working_dir.to_str(), crate_name);
-        let unit_metadata = ~[lltag(tg),
-                             llunused(),
-                             lli32(DW_LANG_RUST),
-                             llstr(crate_name),
-                             llstr(work_dir),
-                             llstr(env!("CFG_VERSION")),
-                             lli1(true), // deprecated: main compile unit
-                             lli1(cx.sess.opts.optimize != session::No),
-                             llstr(~""), // flags (???)
-                             lli32(0) // runtime version (???)
-                            ];
-        let unit_node = llmdnode(unit_metadata);
-        add_named_metadata(cx, ~"llvm.dbg.cu", unit_node);
-        let mdval = @Metadata {
-            node: unit_node,
-            data: CompileUnitMetadata {
-                name: crate_name
-            }
-        };
-        update_cache(cache, tg, compile_unit_metadata(mdval));
+    let (_, work_dir) = get_file_path_and_dir(
+        cx.sess.working_dir.to_str(), crate_name);
+    let unit_metadata = ~[lltag(tg),
+                         llunused(),
+                         lli32(DW_LANG_RUST),
+                         llstr(crate_name),
+                         llstr(work_dir),
+                         llstr(env!("CFG_VERSION")),
+                         lli1(true), // deprecated: main compile unit
+                         lli1(cx.sess.opts.optimize != session::No),
+                         llstr(~""), // flags (???)
+                         lli32(0) // runtime version (???)
+                        ];
+    let unit_node = llmdnode(unit_metadata);
+    add_named_metadata(cx, ~"llvm.dbg.cu", unit_node);
+    let mdval = @Metadata {
+        node: unit_node,
+        data: CompileUnitMetadata {
+            name: crate_name
+        }
+    };
+    update_cache(cache, tg, compile_unit_metadata(mdval));
 
-        return mdval;
-    }
+    return mdval;
 }
 
 fn get_cache(cx: @CrateContext) -> metadata_cache {
@@ -710,113 +704,109 @@ fn create_var(type_tag: int, context: ValueRef, name: &str, file: ValueRef,
 
 pub fn create_local_var(bcx: block, local: @ast::local)
     -> @Metadata<LocalVarMetadata> {
-    unsafe {
-        let cx = bcx.ccx();
-        let cache = get_cache(cx);
-        let tg = AutoVariableTag;
-        match cached_metadata::<@Metadata<LocalVarMetadata>>(
-            cache, tg, |md| md.data.id == local.node.id) {
-          option::Some(md) => return md,
-          option::None => ()
+    let cx = bcx.ccx();
+    let cache = get_cache(cx);
+    let tg = AutoVariableTag;
+    match cached_metadata::<@Metadata<LocalVarMetadata>>(
+        cache, tg, |md| md.data.id == local.node.id) {
+      option::Some(md) => return md,
+      option::None => ()
+    }
+
+    let name = match local.node.pat.node {
+      ast::pat_ident(_, pth, _) => ast_util::path_to_ident(pth),
+      // FIXME this should be handled (#2533)
+      _ => fail!(~"no single variable name for local")
+    };
+    let loc = cx.sess.codemap.lookup_char_pos(local.span.lo);
+    let ty = node_id_type(bcx, local.node.id);
+    let tymd = create_ty(cx, ty, local.node.ty.span);
+    let filemd = create_file(cx, /*bad*/copy loc.file.name);
+    let context = match bcx.parent {
+        None => create_function(bcx.fcx).node,
+        Some(_) => create_block(bcx).node
+    };
+    let mdnode = create_var(tg, context, *cx.sess.str_of(name),
+                            filemd.node, loc.line as int, tymd.node);
+    let mdval = @Metadata {
+        node: mdnode,
+        data: LocalVarMetadata {
+            id: local.node.id
         }
+    };
+    update_cache(cache, AutoVariableTag, local_var_metadata(mdval));
 
-        let name = match local.node.pat.node {
-          ast::pat_ident(_, pth, _) => ast_util::path_to_ident(pth),
-          // FIXME this should be handled (#2533)
-          _ => fail!(~"no single variable name for local")
-        };
-        let loc = cx.sess.codemap.lookup_char_pos(local.span.lo);
-        let ty = node_id_type(bcx, local.node.id);
-        let tymd = create_ty(cx, ty, local.node.ty.span);
-        let filemd = create_file(cx, /*bad*/copy loc.file.name);
-        let context = match bcx.parent {
-            None => create_function(bcx.fcx).node,
-            Some(_) => create_block(bcx).node
-        };
-        let mdnode = create_var(tg, context, *cx.sess.str_of(name),
-                                filemd.node, loc.line as int, tymd.node);
-        let mdval = @Metadata {
-            node: mdnode,
-            data: LocalVarMetadata {
-                id: local.node.id
-            }
-        };
-        update_cache(cache, AutoVariableTag, local_var_metadata(mdval));
-
-        let llptr = match bcx.fcx.lllocals.find(&local.node.id) {
-          option::Some(&local_mem(v)) => v,
-          option::Some(_) => {
-            bcx.tcx().sess.span_bug(local.span, ~"local is bound to \
-                    something weird");
-          }
-          option::None => {
-            match *bcx.fcx.lllocals.get(&local.node.pat.id) {
-              local_imm(v) => v,
-              _ => bcx.tcx().sess.span_bug(local.span, ~"local is bound to \
-                                                         something weird")
-            }
-          }
-        };
-        let declargs = ~[llmdnode(~[llptr]), mdnode];
-        trans::build::Call(bcx, *cx.intrinsics.get(&~"llvm.dbg.declare"),
-                           declargs);
-        return mdval;
-    }
+    let llptr = match bcx.fcx.lllocals.find(&local.node.id) {
+      option::Some(&local_mem(v)) => v,
+      option::Some(_) => {
+        bcx.tcx().sess.span_bug(local.span, ~"local is bound to \
+                something weird");
+      }
+      option::None => {
+        match *bcx.fcx.lllocals.get(&local.node.pat.id) {
+          local_imm(v) => v,
+          _ => bcx.tcx().sess.span_bug(local.span, ~"local is bound to \
+                                                     something weird")
+        }
+      }
+    };
+    let declargs = ~[llmdnode(~[llptr]), mdnode];
+    trans::build::Call(bcx, *cx.intrinsics.get(&~"llvm.dbg.declare"),
+                       declargs);
+    return mdval;
 }
 
 pub fn create_arg(bcx: block, arg: ast::arg, sp: span)
     -> Option<@Metadata<ArgumentMetadata>> {
-    unsafe {
-        let fcx = bcx.fcx, cx = *fcx.ccx;
-        let cache = get_cache(cx);
-        let tg = ArgVariableTag;
-        match cached_metadata::<@Metadata<ArgumentMetadata>>(
-            cache, ArgVariableTag, |md| md.data.id == arg.id) {
-          option::Some(md) => return Some(md),
-          option::None => ()
-        }
+    let fcx = bcx.fcx, cx = *fcx.ccx;
+    let cache = get_cache(cx);
+    let tg = ArgVariableTag;
+    match cached_metadata::<@Metadata<ArgumentMetadata>>(
+        cache, ArgVariableTag, |md| md.data.id == arg.id) {
+      option::Some(md) => return Some(md),
+      option::None => ()
+    }
 
-        let loc = cx.sess.codemap.lookup_char_pos(sp.lo);
-        if loc.file.name == ~"<intrinsic>" {
-            return None;
+    let loc = cx.sess.codemap.lookup_char_pos(sp.lo);
+    if loc.file.name == ~"<intrinsic>" {
+        return None;
+    }
+    let ty = node_id_type(bcx, arg.id);
+    let tymd = create_ty(cx, ty, arg.ty.span);
+    let filemd = create_file(cx, /*bad*/copy loc.file.name);
+    let context = create_function(bcx.fcx);
+
+    match arg.pat.node {
+        ast::pat_ident(_, path, _) => {
+            // XXX: This is wrong; it should work for multiple bindings.
+            let mdnode = create_var(
+                tg,
+                context.node,
+                *cx.sess.str_of(*path.idents.last()),
+                filemd.node,
+                loc.line as int,
+                tymd.node
+            );
+
+            let mdval = @Metadata {
+                node: mdnode,
+                data: ArgumentMetadata {
+                    id: arg.id
+                }
+            };
+            update_cache(cache, tg, argument_metadata(mdval));
+
+            let llptr = match *fcx.llargs.get(&arg.id) {
+              local_mem(v) | local_imm(v) => v,
+            };
+            let declargs = ~[llmdnode(~[llptr]), mdnode];
+            trans::build::Call(bcx,
+                               *cx.intrinsics.get(&~"llvm.dbg.declare"),
+                               declargs);
+            return Some(mdval);
         }
-        let ty = node_id_type(bcx, arg.id);
-        let tymd = create_ty(cx, ty, arg.ty.span);
-        let filemd = create_file(cx, /*bad*/copy loc.file.name);
-        let context = create_function(bcx.fcx);
-
-        match arg.pat.node {
-            ast::pat_ident(_, path, _) => {
-                // XXX: This is wrong; it should work for multiple bindings.
-                let mdnode = create_var(
-                    tg,
-                    context.node,
-                    *cx.sess.str_of(*path.idents.last()),
-                    filemd.node,
-                    loc.line as int,
-                    tymd.node
-                );
-
-                let mdval = @Metadata {
-                    node: mdnode,
-                    data: ArgumentMetadata {
-                        id: arg.id
-                    }
-                };
-                update_cache(cache, tg, argument_metadata(mdval));
-
-                let llptr = match *fcx.llargs.get(&arg.id) {
-                  local_mem(v) | local_imm(v) => v,
-                };
-                let declargs = ~[llmdnode(~[llptr]), mdnode];
-                trans::build::Call(bcx,
-                                   *cx.intrinsics.get(&~"llvm.dbg.declare"),
-                                   declargs);
-                return Some(mdval);
-            }
-            _ => {
-                return None;
-            }
+        _ => {
+            return None;
         }
     }
 }
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index a26c1c1e766..f62e366ebdc 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -300,7 +300,11 @@ struct ctxt_ {
     destructors: @mut HashSet<ast::def_id>,
 
     // Maps a trait onto a mapping from self-ty to impl
-    trait_impls: @mut HashMap<ast::def_id, @mut HashMap<t, @Impl>>
+    trait_impls: @mut HashMap<ast::def_id, @mut HashMap<t, @Impl>>,
+
+    // Set of used unsafe nodes (functions or blocks). Unsafe nodes not
+    // present in this set can be warned about.
+    used_unsafe: @mut HashSet<ast::node_id>,
 }
 
 enum tbox_flag {
@@ -885,7 +889,8 @@ pub fn mk_ctxt(s: session::Session,
         supertraits: @mut HashMap::new(),
         destructor_for_type: @mut HashMap::new(),
         destructors: @mut HashSet::new(),
-        trait_impls: @mut HashMap::new()
+        trait_impls: @mut HashMap::new(),
+        used_unsafe: @mut HashSet::new(),
      }
 }
 
@@ -4309,16 +4314,16 @@ pub fn eval_repeat_count(tcx: ctxt, count_expr: @ast::expr) -> uint {
 }
 
 // Determine what purity to check a nested function under
-pub fn determine_inherited_purity(parent_purity: ast::purity,
-                                       child_purity: ast::purity,
-                                       child_sigil: ast::Sigil)
-                                    -> ast::purity {
+pub fn determine_inherited_purity(parent: (ast::purity, ast::node_id),
+                                  child: (ast::purity, ast::node_id),
+                                  child_sigil: ast::Sigil)
+                                    -> (ast::purity, ast::node_id) {
     // If the closure is a stack closure and hasn't had some non-standard
     // purity inferred for it, then check it under its parent's purity.
     // Otherwise, use its own
     match child_sigil {
-        ast::BorrowedSigil if child_purity == ast::impure_fn => parent_purity,
-        _ => child_purity
+        ast::BorrowedSigil if child.first() == ast::impure_fn => parent,
+        _ => child
     }
 }
 
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 2d1c940103a..5ec4c233bc0 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -179,6 +179,11 @@ pub enum FnKind {
     Vanilla
 }
 
+struct PurityState {
+    purity: ast::purity,
+    from: ast::node_id,
+}
+
 pub struct FnCtxt {
     // var_bindings, locals and next_var_id are shared
     // with any nested functions that capture the environment
@@ -187,7 +192,7 @@ pub struct FnCtxt {
     ret_ty: ty::t,
     // Used by loop bodies that return from the outer function
     indirect_ret_ty: Option<ty::t>,
-    purity: ast::purity,
+    ps: PurityState,
 
     // Sometimes we generate region pointers where the precise region
     // to use is not known. For example, an expression like `&x.f`
@@ -238,7 +243,7 @@ pub fn blank_fn_ctxt(ccx: @mut CrateCtxt,
     @mut FnCtxt {
         ret_ty: rty,
         indirect_ret_ty: None,
-        purity: ast::pure_fn,
+        ps: PurityState { purity: ast::pure_fn, from: 0 },
         region_lb: region_bnd,
         in_scope_regions: @Nil,
         fn_kind: Vanilla,
@@ -265,7 +270,7 @@ pub fn check_bare_fn(ccx: @mut CrateCtxt,
         ty::ty_bare_fn(ref fn_ty) => {
             let fcx =
                 check_fn(ccx, self_info, fn_ty.purity,
-                         &fn_ty.sig, decl, body, Vanilla,
+                         &fn_ty.sig, decl, id, body, Vanilla,
                          @Nil, blank_inherited(ccx));;
 
             vtable::resolve_in_block(fcx, body);
@@ -282,6 +287,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
                 purity: ast::purity,
                 fn_sig: &ty::FnSig,
                 decl: &ast::fn_decl,
+                id: ast::node_id,
                 body: &ast::blk,
                 fn_kind: FnKind,
                 inherited_isr: isr_alist,
@@ -342,7 +348,7 @@ pub fn check_fn(ccx: @mut CrateCtxt,
         @mut FnCtxt {
             ret_ty: ret_ty,
             indirect_ret_ty: indirect_ret_ty,
-            purity: purity,
+            ps: PurityState { purity: purity, from: id },
             region_lb: body.node.id,
             in_scope_regions: isr,
             fn_kind: fn_kind,
@@ -867,8 +873,12 @@ pub impl FnCtxt {
     }
 
     fn require_unsafe(&self, sp: span, op: ~str) {
-        match self.purity {
-          ast::unsafe_fn => {/*ok*/}
+        match self.ps.purity {
+          ast::unsafe_fn => {
+            // ok, but flag that we used the source of unsafeness
+            debug!("flagging %? as a used unsafe source", self.ps.from);
+            self.tcx().used_unsafe.insert(self.ps.from);
+          }
           _ => {
             self.ccx.tcx.sess.span_err(
                 sp,
@@ -1679,12 +1689,13 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
 
         fcx.write_ty(expr.id, fty);
 
-        let inherited_purity =
-            ty::determine_inherited_purity(copy fcx.purity, purity,
+        let (inherited_purity, id) =
+            ty::determine_inherited_purity((fcx.ps.purity, fcx.ps.from),
+                                           (purity, expr.id),
                                            sigil);
 
         check_fn(fcx.ccx, None, inherited_purity, &fty_sig,
-                 decl, body, fn_kind, fcx.in_scope_regions, fcx.inh);
+                 decl, id, body, fn_kind, fcx.in_scope_regions, fcx.inh);
     }
 
 
@@ -2923,8 +2934,11 @@ pub fn check_block_with_expected(fcx0: @mut FnCtxt,
                                  blk: &ast::blk,
                                  expected: Option<ty::t>) {
     let fcx = match blk.node.rules {
-      ast::unsafe_blk => @mut FnCtxt {purity: ast::unsafe_fn,.. copy *fcx0},
-      ast::default_blk => fcx0
+        ast::unsafe_blk => @mut FnCtxt {
+            ps: PurityState { purity: ast::unsafe_fn, from: blk.node.id },
+            .. copy *fcx0
+        },
+        ast::default_blk => fcx0
     };
     do fcx.with_region_lb(blk.node.id) {
         let mut warned = false;
diff --git a/src/libstd/c_vec.rs b/src/libstd/c_vec.rs
index 113c8130349..a59c76c809b 100644
--- a/src/libstd/c_vec.rs
+++ b/src/libstd/c_vec.rs
@@ -57,11 +57,9 @@ struct DtorRes {
 #[unsafe_destructor]
 impl Drop for DtorRes {
     fn finalize(&self) {
-        unsafe {
-            match self.dtor {
-                option::None => (),
-                option::Some(f) => f()
-            }
+        match self.dtor {
+            option::None => (),
+            option::Some(f) => f()
         }
     }
 }
@@ -84,7 +82,7 @@ fn DtorRes(dtor: Option<@fn()>) -> DtorRes {
  * * base - A foreign pointer to a buffer
  * * len - The number of elements in the buffer
  */
-pub unsafe fn CVec<T>(base: *mut T, len: uint) -> CVec<T> {
+pub fn CVec<T>(base: *mut T, len: uint) -> CVec<T> {
     return CVec{
         base: base,
         len: len,
@@ -103,7 +101,7 @@ pub unsafe fn CVec<T>(base: *mut T, len: uint) -> CVec<T> {
  * * dtor - A function to run when the value is destructed, useful
  *          for freeing the buffer, etc.
  */
-pub unsafe fn c_vec_with_dtor<T>(base: *mut T, len: uint, dtor: @fn())
+pub fn c_vec_with_dtor<T>(base: *mut T, len: uint, dtor: @fn())
   -> CVec<T> {
     return CVec{
         base: base,
@@ -144,7 +142,7 @@ pub fn set<T:Copy>(t: CVec<T>, ofs: uint, v: T) {
 pub fn len<T>(t: CVec<T>) -> uint { t.len }
 
 /// Returns a pointer to the first element of the vector
-pub unsafe fn ptr<T>(t: CVec<T>) -> *mut T { t.base }
+pub fn ptr<T>(t: CVec<T>) -> *mut T { t.base }
 
 #[cfg(test)]
 mod tests {
diff --git a/src/libstd/getopts.rs b/src/libstd/getopts.rs
index df37c48ebe8..b1e80718d8f 100644
--- a/src/libstd/getopts.rs
+++ b/src/libstd/getopts.rs
@@ -223,128 +223,126 @@ pub type Result = result::Result<Matches, Fail_>;
  * Use <fail_str> to get an error message.
  */
 pub fn getopts(args: &[~str], opts: &[Opt]) -> Result {
-    unsafe {
-        let n_opts = opts.len();
-        fn f(_x: uint) -> ~[Optval] { return ~[]; }
-        let mut vals = vec::from_fn(n_opts, f);
-        let mut free: ~[~str] = ~[];
-        let l = args.len();
-        let mut i = 0;
-        while i < l {
-            let cur = args[i];
-            let curlen = cur.len();
-            if !is_arg(cur) {
-                free.push(cur);
-            } else if cur == ~"--" {
-                let mut j = i + 1;
-                while j < l { free.push(args[j]); j += 1; }
-                break;
-            } else {
-                let mut names;
-                let mut i_arg = None;
-                if cur[1] == '-' as u8 {
-                    let tail = str::slice(cur, 2, curlen).to_owned();
-                    let mut tail_eq = ~[];
-                    for str::each_splitn_char(tail, '=', 1) |s| { tail_eq.push(s.to_owned()) }
-                    if tail_eq.len() <= 1 {
-                        names = ~[Long(tail)];
-                    } else {
-                        names =
-                            ~[Long(tail_eq[0])];
-                        i_arg = Some(tail_eq[1]);
-                    }
+    let n_opts = opts.len();
+    fn f(_x: uint) -> ~[Optval] { return ~[]; }
+    let mut vals = vec::from_fn(n_opts, f);
+    let mut free: ~[~str] = ~[];
+    let l = args.len();
+    let mut i = 0;
+    while i < l {
+        let cur = args[i];
+        let curlen = cur.len();
+        if !is_arg(cur) {
+            free.push(cur);
+        } else if cur == ~"--" {
+            let mut j = i + 1;
+            while j < l { free.push(args[j]); j += 1; }
+            break;
+        } else {
+            let mut names;
+            let mut i_arg = None;
+            if cur[1] == '-' as u8 {
+                let tail = str::slice(cur, 2, curlen).to_owned();
+                let mut tail_eq = ~[];
+                for str::each_splitn_char(tail, '=', 1) |s| { tail_eq.push(s.to_owned()) }
+                if tail_eq.len() <= 1 {
+                    names = ~[Long(tail)];
                 } else {
-                    let mut j = 1;
-                    let mut last_valid_opt_id = None;
-                    names = ~[];
-                    while j < curlen {
-                        let range = str::char_range_at(cur, j);
-                        let opt = Short(range.ch);
-
-                        /* In a series of potential options (eg. -aheJ), if we
-                           see one which takes an argument, we assume all
-                           subsequent characters make up the argument. This
-                           allows options such as -L/usr/local/lib/foo to be
-                           interpreted correctly
-                        */
-
-                        match find_opt(opts, opt) {
-                          Some(id) => last_valid_opt_id = Some(id),
-                          None => {
-                            let arg_follows =
-                                last_valid_opt_id.is_some() &&
-                                match opts[last_valid_opt_id.get()]
-                                  .hasarg {
-
-                                  Yes | Maybe => true,
-                                  No => false
-                                };
-                            if arg_follows && j < curlen {
-                                i_arg = Some(cur.slice(j, curlen).to_owned());
-                                break;
-                            } else {
-                                last_valid_opt_id = None;
-                            }
-                          }
-                        }
-                        names.push(opt);
-                        j = range.next;
-                    }
+                    names =
+                        ~[Long(tail_eq[0])];
+                    i_arg = Some(tail_eq[1]);
                 }
-                let mut name_pos = 0;
-                for names.each() |nm| {
-                    name_pos += 1;
-                    let optid = match find_opt(opts, *nm) {
-                      Some(id) => id,
-                      None => return Err(UnrecognizedOption(name_str(nm)))
-                    };
-                    match opts[optid].hasarg {
-                      No => {
-                        if !i_arg.is_none() {
-                            return Err(UnexpectedArgument(name_str(nm)));
+            } else {
+                let mut j = 1;
+                let mut last_valid_opt_id = None;
+                names = ~[];
+                while j < curlen {
+                    let range = str::char_range_at(cur, j);
+                    let opt = Short(range.ch);
+
+                    /* In a series of potential options (eg. -aheJ), if we
+                       see one which takes an argument, we assume all
+                       subsequent characters make up the argument. This
+                       allows options such as -L/usr/local/lib/foo to be
+                       interpreted correctly
+                    */
+
+                    match find_opt(opts, opt) {
+                      Some(id) => last_valid_opt_id = Some(id),
+                      None => {
+                        let arg_follows =
+                            last_valid_opt_id.is_some() &&
+                            match opts[last_valid_opt_id.get()]
+                              .hasarg {
+
+                              Yes | Maybe => true,
+                              No => false
+                            };
+                        if arg_follows && j < curlen {
+                            i_arg = Some(cur.slice(j, curlen).to_owned());
+                            break;
+                        } else {
+                            last_valid_opt_id = None;
                         }
-                        vals[optid].push(Given);
-                      }
-                      Maybe => {
-                        if !i_arg.is_none() {
-                            vals[optid].push(Val(i_arg.get()));
-                        } else if name_pos < names.len() ||
-                                      i + 1 == l || is_arg(args[i + 1]) {
-                            vals[optid].push(Given);
-                        } else { i += 1; vals[optid].push(Val(args[i])); }
-                      }
-                      Yes => {
-                        if !i_arg.is_none() {
-                            vals[optid].push(Val(i_arg.get()));
-                        } else if i + 1 == l {
-                            return Err(ArgumentMissing(name_str(nm)));
-                        } else { i += 1; vals[optid].push(Val(args[i])); }
                       }
                     }
+                    names.push(opt);
+                    j = range.next;
                 }
             }
-            i += 1;
-        }
-        i = 0u;
-        while i < n_opts {
-            let n = vals[i].len();
-            let occ = opts[i].occur;
-            if occ == Req {
-                if n == 0 {
-                    return Err(OptionMissing(name_str(&(opts[i].name))));
+            let mut name_pos = 0;
+            for names.each() |nm| {
+                name_pos += 1;
+                let optid = match find_opt(opts, *nm) {
+                  Some(id) => id,
+                  None => return Err(UnrecognizedOption(name_str(nm)))
+                };
+                match opts[optid].hasarg {
+                  No => {
+                    if !i_arg.is_none() {
+                        return Err(UnexpectedArgument(name_str(nm)));
+                    }
+                    vals[optid].push(Given);
+                  }
+                  Maybe => {
+                    if !i_arg.is_none() {
+                        vals[optid].push(Val(i_arg.get()));
+                    } else if name_pos < names.len() ||
+                                  i + 1 == l || is_arg(args[i + 1]) {
+                        vals[optid].push(Given);
+                    } else { i += 1; vals[optid].push(Val(args[i])); }
+                  }
+                  Yes => {
+                    if !i_arg.is_none() {
+                        vals[optid].push(Val(i_arg.get()));
+                    } else if i + 1 == l {
+                        return Err(ArgumentMissing(name_str(nm)));
+                    } else { i += 1; vals[optid].push(Val(args[i])); }
+                  }
                 }
             }
-            if occ != Multi {
-                if n > 1 {
-                    return Err(OptionDuplicated(name_str(&(opts[i].name))));
-                }
+        }
+        i += 1;
+    }
+    i = 0u;
+    while i < n_opts {
+        let n = vals[i].len();
+        let occ = opts[i].occur;
+        if occ == Req {
+            if n == 0 {
+                return Err(OptionMissing(name_str(&(opts[i].name))));
+            }
+        }
+        if occ != Multi {
+            if n > 1 {
+                return Err(OptionDuplicated(name_str(&(opts[i].name))));
             }
-            i += 1;
         }
-        return Ok(Matches {opts: vec::from_slice(opts),
-                   vals: vals,
-                   free: free});
+        i += 1;
     }
+    return Ok(Matches {opts: vec::from_slice(opts),
+               vals: vals,
+               free: free});
 }
 
 fn opt_vals(mm: &Matches, nm: &str) -> ~[Optval] {
diff --git a/src/libstd/net_ip.rs b/src/libstd/net_ip.rs
index 6403e0eb5c4..e920ff20ac5 100644
--- a/src/libstd/net_ip.rs
+++ b/src/libstd/net_ip.rs
@@ -116,35 +116,33 @@ pub fn get_addr(node: &str, iotask: &iotask)
     let mut output_ch = Some(SharedChan(output_ch));
     do str::as_buf(node) |node_ptr, len| {
         let output_ch = output_ch.swap_unwrap();
-        unsafe {
-            debug!("slice len %?", len);
-            let handle = create_uv_getaddrinfo_t();
-            let handle_ptr = ptr::addr_of(&handle);
-            let handle_data = GetAddrData {
-                output_ch: output_ch.clone()
-            };
-            let handle_data_ptr = ptr::addr_of(&handle_data);
-            do interact(iotask) |loop_ptr| {
-                unsafe {
-                    let result = uv_getaddrinfo(
-                        loop_ptr,
-                        handle_ptr,
-                        get_addr_cb,
-                        node_ptr,
-                        ptr::null(),
-                        ptr::null());
-                    match result {
-                        0i32 => {
-                            set_data_for_req(handle_ptr, handle_data_ptr);
-                        }
-                        _ => {
-                            output_ch.send(result::Err(GetAddrUnknownError));
-                        }
+        debug!("slice len %?", len);
+        let handle = create_uv_getaddrinfo_t();
+        let handle_ptr = ptr::addr_of(&handle);
+        let handle_data = GetAddrData {
+            output_ch: output_ch.clone()
+        };
+        let handle_data_ptr = ptr::addr_of(&handle_data);
+        do interact(iotask) |loop_ptr| {
+            unsafe {
+                let result = uv_getaddrinfo(
+                    loop_ptr,
+                    handle_ptr,
+                    get_addr_cb,
+                    node_ptr,
+                    ptr::null(),
+                    ptr::null());
+                match result {
+                    0i32 => {
+                        set_data_for_req(handle_ptr, handle_data_ptr);
+                    }
+                    _ => {
+                        output_ch.send(result::Err(GetAddrUnknownError));
                     }
                 }
-            };
-            output_po.recv()
-        }
+            }
+        };
+        output_po.recv()
     }
 }
 
diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs
index 9630351b5c9..a8b2723bcfb 100644
--- a/src/libstd/net_tcp.rs
+++ b/src/libstd/net_tcp.rs
@@ -57,9 +57,7 @@ pub struct TcpSocket {
 #[unsafe_destructor]
 impl Drop for TcpSocket {
     fn finalize(&self) {
-        unsafe {
-            tear_down_socket_data(self.socket_data)
-        }
+        tear_down_socket_data(self.socket_data)
     }
 }
 
@@ -302,11 +300,10 @@ pub fn connect(input_ip: ip::IpAddr, port: uint,
  * `TcpErrData` value as the `Err` variant
  */
 pub fn write(sock: &TcpSocket, raw_write_data: ~[u8])
-    -> result::Result<(), TcpErrData> {
-    unsafe {
-        let socket_data_ptr = ptr::addr_of(&(*(sock.socket_data)));
-        write_common_impl(socket_data_ptr, raw_write_data)
-    }
+    -> result::Result<(), TcpErrData>
+{
+    let socket_data_ptr = ptr::addr_of(&(*(sock.socket_data)));
+    write_common_impl(socket_data_ptr, raw_write_data)
 }
 
 /**
@@ -341,13 +338,12 @@ pub fn write(sock: &TcpSocket, raw_write_data: ~[u8])
  * value as the `Err` variant
  */
 pub fn write_future(sock: &TcpSocket, raw_write_data: ~[u8])
-    -> future::Future<result::Result<(), TcpErrData>> {
-    unsafe {
-        let socket_data_ptr = ptr::addr_of(&(*(sock.socket_data)));
-        do future_spawn {
-            let data_copy = copy(raw_write_data);
-            write_common_impl(socket_data_ptr, data_copy)
-        }
+    -> future::Future<result::Result<(), TcpErrData>>
+{
+    let socket_data_ptr = ptr::addr_of(&(*(sock.socket_data)));
+    do future_spawn {
+        let data_copy = copy(raw_write_data);
+        write_common_impl(socket_data_ptr, data_copy)
     }
 }
 
@@ -369,10 +365,8 @@ pub fn write_future(sock: &TcpSocket, raw_write_data: ~[u8])
 pub fn read_start(sock: &TcpSocket)
     -> result::Result<@Port<
         result::Result<~[u8], TcpErrData>>, TcpErrData> {
-    unsafe {
-        let socket_data = ptr::addr_of(&(*(sock.socket_data)));
-        read_start_common_impl(socket_data)
-    }
+    let socket_data = ptr::addr_of(&(*(sock.socket_data)));
+    read_start_common_impl(socket_data)
 }
 
 /**
@@ -382,12 +376,9 @@ pub fn read_start(sock: &TcpSocket)
  *
  * * `sock` - a `net::tcp::TcpSocket` that you wish to stop reading on
  */
-pub fn read_stop(sock: &TcpSocket) ->
-    result::Result<(), TcpErrData> {
-    unsafe {
-        let socket_data = ptr::addr_of(&(*sock.socket_data));
-        read_stop_common_impl(socket_data)
-    }
+pub fn read_stop(sock: &TcpSocket) -> result::Result<(), TcpErrData> {
+    let socket_data = ptr::addr_of(&(*sock.socket_data));
+    read_stop_common_impl(socket_data)
 }
 
 /**
@@ -654,150 +645,148 @@ fn listen_common(host_ip: ip::IpAddr,
                  on_establish_cb: ~fn(SharedChan<Option<TcpErrData>>),
                  on_connect_cb: ~fn(*uv::ll::uv_tcp_t))
               -> result::Result<(), TcpListenErrData> {
-    unsafe {
-        let (stream_closed_po, stream_closed_ch) = stream::<()>();
-        let stream_closed_ch = SharedChan(stream_closed_ch);
-        let (kill_po, kill_ch) = stream::<Option<TcpErrData>>();
-        let kill_ch = SharedChan(kill_ch);
-        let server_stream = uv::ll::tcp_t();
-        let server_stream_ptr = ptr::addr_of(&server_stream);
-        let server_data: TcpListenFcData = TcpListenFcData {
-            server_stream_ptr: server_stream_ptr,
-            stream_closed_ch: stream_closed_ch,
-            kill_ch: kill_ch.clone(),
-            on_connect_cb: on_connect_cb,
-            iotask: iotask.clone(),
-            ipv6: match &host_ip {
-                &ip::Ipv4(_) => { false }
-                &ip::Ipv6(_) => { true }
-            },
-            mut active: true
-        };
-        let server_data_ptr = ptr::addr_of(&server_data);
-
-        let (setup_po, setup_ch) = stream();
-
-        // this is to address a compiler warning about
-        // an implicit copy.. it seems that double nested
-        // will defeat a move sigil, as is done to the host_ip
-        // arg above.. this same pattern works w/o complaint in
-        // tcp::connect (because the iotask::interact cb isn't
-        // nested within a core::comm::listen block)
-        let loc_ip = copy(host_ip);
-        do iotask::interact(iotask) |loop_ptr| {
-            unsafe {
-                match uv::ll::tcp_init(loop_ptr, server_stream_ptr) {
-                    0i32 => {
-                        uv::ll::set_data_for_uv_handle(
-                            server_stream_ptr,
-                            server_data_ptr);
-                        let addr_str = ip::format_addr(&loc_ip);
-                        let bind_result = match loc_ip {
-                            ip::Ipv4(ref addr) => {
-                                debug!("addr: %?", addr);
-                                let in_addr = uv::ll::ip4_addr(
-                                    addr_str,
-                                    port as int);
-                                uv::ll::tcp_bind(server_stream_ptr,
-                                                 ptr::addr_of(&in_addr))
-                            }
-                            ip::Ipv6(ref addr) => {
-                                debug!("addr: %?", addr);
-                                let in_addr = uv::ll::ip6_addr(
-                                    addr_str,
-                                    port as int);
-                                uv::ll::tcp_bind6(server_stream_ptr,
-                                                  ptr::addr_of(&in_addr))
-                            }
-                        };
-                        match bind_result {
-                            0i32 => {
-                                match uv::ll::listen(
-                                    server_stream_ptr,
-                                    backlog as libc::c_int,
-                                    tcp_lfc_on_connection_cb) {
-                                    0i32 => setup_ch.send(None),
-                                    _ => {
-                                        debug!(
-                                            "failure to uv_tcp_init");
-                                        let err_data =
-                                            uv::ll::get_last_err_data(
-                                                loop_ptr);
-                                        setup_ch.send(Some(err_data));
-                                    }
+    let (stream_closed_po, stream_closed_ch) = stream::<()>();
+    let stream_closed_ch = SharedChan(stream_closed_ch);
+    let (kill_po, kill_ch) = stream::<Option<TcpErrData>>();
+    let kill_ch = SharedChan(kill_ch);
+    let server_stream = uv::ll::tcp_t();
+    let server_stream_ptr = ptr::addr_of(&server_stream);
+    let server_data: TcpListenFcData = TcpListenFcData {
+        server_stream_ptr: server_stream_ptr,
+        stream_closed_ch: stream_closed_ch,
+        kill_ch: kill_ch.clone(),
+        on_connect_cb: on_connect_cb,
+        iotask: iotask.clone(),
+        ipv6: match &host_ip {
+            &ip::Ipv4(_) => { false }
+            &ip::Ipv6(_) => { true }
+        },
+        mut active: true
+    };
+    let server_data_ptr = ptr::addr_of(&server_data);
+
+    let (setup_po, setup_ch) = stream();
+
+    // this is to address a compiler warning about
+    // an implicit copy.. it seems that double nested
+    // will defeat a move sigil, as is done to the host_ip
+    // arg above.. this same pattern works w/o complaint in
+    // tcp::connect (because the iotask::interact cb isn't
+    // nested within a core::comm::listen block)
+    let loc_ip = copy(host_ip);
+    do iotask::interact(iotask) |loop_ptr| {
+        unsafe {
+            match uv::ll::tcp_init(loop_ptr, server_stream_ptr) {
+                0i32 => {
+                    uv::ll::set_data_for_uv_handle(
+                        server_stream_ptr,
+                        server_data_ptr);
+                    let addr_str = ip::format_addr(&loc_ip);
+                    let bind_result = match loc_ip {
+                        ip::Ipv4(ref addr) => {
+                            debug!("addr: %?", addr);
+                            let in_addr = uv::ll::ip4_addr(
+                                addr_str,
+                                port as int);
+                            uv::ll::tcp_bind(server_stream_ptr,
+                                             ptr::addr_of(&in_addr))
+                        }
+                        ip::Ipv6(ref addr) => {
+                            debug!("addr: %?", addr);
+                            let in_addr = uv::ll::ip6_addr(
+                                addr_str,
+                                port as int);
+                            uv::ll::tcp_bind6(server_stream_ptr,
+                                              ptr::addr_of(&in_addr))
+                        }
+                    };
+                    match bind_result {
+                        0i32 => {
+                            match uv::ll::listen(
+                                server_stream_ptr,
+                                backlog as libc::c_int,
+                                tcp_lfc_on_connection_cb) {
+                                0i32 => setup_ch.send(None),
+                                _ => {
+                                    debug!(
+                                        "failure to uv_tcp_init");
+                                    let err_data =
+                                        uv::ll::get_last_err_data(
+                                            loop_ptr);
+                                    setup_ch.send(Some(err_data));
                                 }
                             }
-                            _ => {
-                                debug!("failure to uv_tcp_bind");
-                                let err_data = uv::ll::get_last_err_data(
-                                    loop_ptr);
-                                setup_ch.send(Some(err_data));
-                            }
                         }
-                    }
-                    _ => {
-                        debug!("failure to uv_tcp_bind");
-                        let err_data = uv::ll::get_last_err_data(
-                            loop_ptr);
-                        setup_ch.send(Some(err_data));
+                        _ => {
+                            debug!("failure to uv_tcp_bind");
+                            let err_data = uv::ll::get_last_err_data(
+                                loop_ptr);
+                            setup_ch.send(Some(err_data));
+                        }
                     }
                 }
+                _ => {
+                    debug!("failure to uv_tcp_bind");
+                    let err_data = uv::ll::get_last_err_data(
+                        loop_ptr);
+                    setup_ch.send(Some(err_data));
+                }
             }
         }
+    }
 
-        let setup_result = setup_po.recv();
+    let setup_result = setup_po.recv();
 
-        match setup_result {
-            Some(ref err_data) => {
-                do iotask::interact(iotask) |loop_ptr| {
-                    unsafe {
-                        debug!(
-                            "tcp::listen post-kill recv hl interact %?",
-                                 loop_ptr);
-                        (*server_data_ptr).active = false;
-                        uv::ll::close(server_stream_ptr, tcp_lfc_close_cb);
-                    }
-                };
-                stream_closed_po.recv();
-                match err_data.err_name {
-                    ~"EACCES" => {
-                        debug!("Got EACCES error");
-                        result::Err(AccessDenied)
-                    }
-                    ~"EADDRINUSE" => {
-                        debug!("Got EADDRINUSE error");
-                        result::Err(AddressInUse)
-                    }
-                    _ => {
-                        debug!("Got '%s' '%s' libuv error",
-                                        err_data.err_name, err_data.err_msg);
-                        result::Err(
-                            GenericListenErr(err_data.err_name,
-                                             err_data.err_msg))
-                    }
+    match setup_result {
+        Some(ref err_data) => {
+            do iotask::interact(iotask) |loop_ptr| {
+                unsafe {
+                    debug!(
+                        "tcp::listen post-kill recv hl interact %?",
+                             loop_ptr);
+                    (*server_data_ptr).active = false;
+                    uv::ll::close(server_stream_ptr, tcp_lfc_close_cb);
+                }
+            };
+            stream_closed_po.recv();
+            match err_data.err_name {
+                ~"EACCES" => {
+                    debug!("Got EACCES error");
+                    result::Err(AccessDenied)
+                }
+                ~"EADDRINUSE" => {
+                    debug!("Got EADDRINUSE error");
+                    result::Err(AddressInUse)
+                }
+                _ => {
+                    debug!("Got '%s' '%s' libuv error",
+                                    err_data.err_name, err_data.err_msg);
+                    result::Err(
+                        GenericListenErr(err_data.err_name,
+                                         err_data.err_msg))
                 }
             }
-            None => {
-                on_establish_cb(kill_ch.clone());
-                let kill_result = kill_po.recv();
-                do iotask::interact(iotask) |loop_ptr| {
-                    unsafe {
-                        debug!(
-                            "tcp::listen post-kill recv hl interact %?",
-                                 loop_ptr);
-                        (*server_data_ptr).active = false;
-                        uv::ll::close(server_stream_ptr, tcp_lfc_close_cb);
-                    }
-                };
-                stream_closed_po.recv();
-                match kill_result {
-                    // some failure post bind/listen
-                    Some(ref err_data) => result::Err(GenericListenErr(
-                        err_data.err_name,
-                        err_data.err_msg)),
-                    // clean exit
-                    None => result::Ok(())
+        }
+        None => {
+            on_establish_cb(kill_ch.clone());
+            let kill_result = kill_po.recv();
+            do iotask::interact(iotask) |loop_ptr| {
+                unsafe {
+                    debug!(
+                        "tcp::listen post-kill recv hl interact %?",
+                             loop_ptr);
+                    (*server_data_ptr).active = false;
+                    uv::ll::close(server_stream_ptr, tcp_lfc_close_cb);
                 }
+            };
+            stream_closed_po.recv();
+            match kill_result {
+                // some failure post bind/listen
+                Some(ref err_data) => result::Err(GenericListenErr(
+                    err_data.err_name,
+                    err_data.err_msg)),
+                // clean exit
+                None => result::Ok(())
             }
         }
     }
@@ -1382,9 +1371,7 @@ extern fn stream_error_close_cb(handle: *uv::ll::uv_tcp_t) {
 }
 
 extern fn tcp_connect_close_cb(handle: *uv::ll::uv_tcp_t) {
-    unsafe {
-        debug!("closed client tcp handle %?", handle);
-    }
+    debug!("closed client tcp handle %?", handle);
 }
 
 extern fn tcp_connect_on_connect_cb(connect_req_ptr: *uv::ll::uv_connect_t,
diff --git a/src/libstd/net_url.rs b/src/libstd/net_url.rs
index b8e0d9d9b2a..9b8b0f9be0b 100644
--- a/src/libstd/net_url.rs
+++ b/src/libstd/net_url.rs
@@ -118,8 +118,7 @@ fn encode_inner(s: &str, full_url: bool) -> ~str {
  * This function is compliant with RFC 3986.
  */
 pub fn encode(s: &str) -> ~str {
-    // FIXME(#3722): unsafe only because encode_inner does (string) IO
-    unsafe {encode_inner(s, true)}
+    encode_inner(s, true)
 }
 
 /**
@@ -130,8 +129,7 @@ pub fn encode(s: &str) -> ~str {
  */
 
 pub fn encode_component(s: &str) -> ~str {
-    // FIXME(#3722): unsafe only because encode_inner does (string) IO
-    unsafe {encode_inner(s, false)}
+    encode_inner(s, false)
 }
 
 fn decode_inner(s: &str, full_url: bool) -> ~str {
@@ -178,16 +176,14 @@ fn decode_inner(s: &str, full_url: bool) -> ~str {
  * This will only decode escape sequences generated by encode.
  */
 pub fn decode(s: &str) -> ~str {
-    // FIXME(#3722): unsafe only because decode_inner does (string) IO
-    unsafe {decode_inner(s, true)}
+    decode_inner(s, true)
 }
 
 /**
  * Decode a string encoded with percent encoding.
  */
 pub fn decode_component(s: &str) -> ~str {
-    // FIXME(#3722): unsafe only because decode_inner does (string) IO
-    unsafe {decode_inner(s, false)}
+    decode_inner(s, false)
 }
 
 fn encode_plus(s: &str) -> ~str {
@@ -301,18 +297,15 @@ fn split_char_first(s: &str, c: char) -> (~str, ~str) {
     let len = str::len(s);
     let mut index = len;
     let mut mat = 0;
-    // FIXME(#3722): unsafe only because decode_inner does (string) IO
-    unsafe {
-        do io::with_str_reader(s) |rdr| {
-            let mut ch;
-            while !rdr.eof() {
-                ch = rdr.read_byte() as char;
-                if ch == c {
-                    // found a match, adjust markers
-                    index = rdr.tell()-1;
-                    mat = 1;
-                    break;
-                }
+    do io::with_str_reader(s) |rdr| {
+        let mut ch;
+        while !rdr.eof() {
+            ch = rdr.read_byte() as char;
+            if ch == c {
+                // found a match, adjust markers
+                index = rdr.tell()-1;
+                mat = 1;
+                break;
             }
         }
     }
@@ -346,29 +339,25 @@ fn query_from_str(rawquery: &str) -> Query {
     if str::len(rawquery) != 0 {
         for str::each_split_char(rawquery, '&') |p| {
             let (k, v) = split_char_first(p, '=');
-            // FIXME(#3722): unsafe only because decode_inner does (string) IO
-            unsafe {query.push((decode_component(k), decode_component(v)));}
+            query.push((decode_component(k), decode_component(v)));
         };
     }
     return query;
 }
 
 pub fn query_to_str(query: &Query) -> ~str {
-    unsafe {
-        // FIXME(#3722): unsafe only because decode_inner does (string) IO
-        let mut strvec = ~[];
-        for query.each |kv| {
-            match kv {
-                &(ref k, ref v) => {
-                    strvec.push(fmt!("%s=%s",
-                        encode_component(*k),
-                        encode_component(*v))
-                    );
-                }
+    let mut strvec = ~[];
+    for query.each |kv| {
+        match kv {
+            &(ref k, ref v) => {
+                strvec.push(fmt!("%s=%s",
+                    encode_component(*k),
+                    encode_component(*v))
+                );
             }
         }
-        return str::connect(strvec, ~"&");
     }
+    return str::connect(strvec, ~"&");
 }
 
 // returns the scheme and the rest of the url, or a parsing error
diff --git a/src/libstd/rl.rs b/src/libstd/rl.rs
index a8b25767ce5..9f9f2323d27 100644
--- a/src/libstd/rl.rs
+++ b/src/libstd/rl.rs
@@ -59,12 +59,10 @@ pub unsafe fn load_history(file: ~str) -> bool {
 /// Print out a prompt and then wait for input and return it
 pub unsafe fn read(prompt: ~str) -> Option<~str> {
     do str::as_c_str(prompt) |buf| {
-        unsafe {
-            let line = rustrt::linenoise(buf);
+        let line = rustrt::linenoise(buf);
 
-            if line.is_null() { None }
-            else { Some(str::raw::from_c_str(line)) }
-        }
+        if line.is_null() { None }
+        else { Some(str::raw::from_c_str(line)) }
     }
 }
 
@@ -74,22 +72,20 @@ fn complete_key(_v: @CompletionCb) {}
 
 /// Bind to the main completion callback
 pub unsafe fn complete(cb: CompletionCb) {
-    unsafe {
-        task::local_data::local_data_set(complete_key, @(cb));
-
-        extern fn callback(line: *c_char, completions: *()) {
-            unsafe {
-                let cb = *task::local_data::local_data_get(complete_key)
-                    .get();
-
-                do cb(str::raw::from_c_str(line)) |suggestion| {
-                    do str::as_c_str(suggestion) |buf| {
-                        rustrt::linenoiseAddCompletion(completions, buf);
-                    }
+    task::local_data::local_data_set(complete_key, @(cb));
+
+    extern fn callback(line: *c_char, completions: *()) {
+        unsafe {
+            let cb = *task::local_data::local_data_get(complete_key)
+                .get();
+
+            do cb(str::raw::from_c_str(line)) |suggestion| {
+                do str::as_c_str(suggestion) |buf| {
+                    rustrt::linenoiseAddCompletion(completions, buf);
                 }
             }
         }
-
-        rustrt::linenoiseSetCompletionCallback(callback);
     }
+
+    rustrt::linenoiseSetCompletionCallback(callback);
 }
diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs
index 232f46b6676..653283f2e78 100644
--- a/src/libstd/rope.rs
+++ b/src/libstd/rope.rs
@@ -862,17 +862,15 @@ pub mod node {
      * This function executes in linear time.
      */
     pub fn flatten(node: @Node) -> @Node {
-        unsafe {
-            match (*node) {
-                Leaf(_) => node,
-                Concat(ref x) => {
-                    @Leaf(Leaf {
-                        byte_offset: 0u,
-                        byte_len: x.byte_len,
-                        char_len: x.char_len,
-                        content: @serialize_node(node),
-                    })
-                }
+        match (*node) {
+            Leaf(_) => node,
+            Concat(ref x) => {
+                @Leaf(Leaf {
+                    byte_offset: 0u,
+                    byte_len: x.byte_len,
+                    char_len: x.char_len,
+                    content: @serialize_node(node),
+                })
             }
         }
     }
diff --git a/src/libstd/sync.rs b/src/libstd/sync.rs
index 1bfdd7f99d5..f2de8213a1b 100644
--- a/src/libstd/sync.rs
+++ b/src/libstd/sync.rs
@@ -101,17 +101,15 @@ fn new_sem_and_signal(count: int, num_condvars: uint)
 pub impl<Q:Owned> Sem<Q> {
     fn acquire(&self) {
         let mut waiter_nobe = None;
-        unsafe {
-            do (**self).with |state| {
-                state.count -= 1;
-                if state.count < 0 {
-                    // Create waiter nobe.
-                    let (WaitEnd, SignalEnd) = comm::oneshot();
-                    // Tell outer scope we need to block.
-                    waiter_nobe = Some(WaitEnd);
-                    // Enqueue ourself.
-                    state.waiters.tail.send(SignalEnd);
-                }
+        do (**self).with |state| {
+            state.count -= 1;
+            if state.count < 0 {
+                // Create waiter nobe.
+                let (WaitEnd, SignalEnd) = comm::oneshot();
+                // Tell outer scope we need to block.
+                waiter_nobe = Some(WaitEnd);
+                // Enqueue ourself.
+                state.waiters.tail.send(SignalEnd);
             }
         }
         // Uncomment if you wish to test for sem races. Not valgrind-friendly.
@@ -122,12 +120,10 @@ pub impl<Q:Owned> Sem<Q> {
         }
     }
     fn release(&self) {
-        unsafe {
-            do (**self).with |state| {
-                state.count += 1;
-                if state.count <= 0 {
-                    signal_waitqueue(&state.waiters);
-                }
+        do (**self).with |state| {
+            state.count += 1;
+            if state.count <= 0 {
+                signal_waitqueue(&state.waiters);
             }
         }
     }
@@ -169,9 +165,7 @@ struct SemReleaseGeneric<'self, Q> { sem: &'self Sem<Q> }
 #[unsafe_destructor]
 impl<'self, Q:Owned> Drop for SemReleaseGeneric<'self, Q> {
     fn finalize(&self) {
-        unsafe {
-            self.sem.release();
-        }
+        self.sem.release();
     }
 }
 
@@ -291,13 +285,11 @@ pub impl<'self> Condvar<'self> {
     fn signal_on(&self, condvar_id: uint) -> bool {
         let mut out_of_bounds = None;
         let mut result = false;
-        unsafe {
-            do (**self.sem).with |state| {
-                if condvar_id < vec::len(state.blocked) {
-                    result = signal_waitqueue(&state.blocked[condvar_id]);
-                } else {
-                    out_of_bounds = Some(vec::len(state.blocked));
-                }
+        do (**self.sem).with |state| {
+            if condvar_id < vec::len(state.blocked) {
+                result = signal_waitqueue(&state.blocked[condvar_id]);
+            } else {
+                out_of_bounds = Some(vec::len(state.blocked));
             }
         }
         do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") {
@@ -312,17 +304,15 @@ pub impl<'self> Condvar<'self> {
     fn broadcast_on(&self, condvar_id: uint) -> uint {
         let mut out_of_bounds = None;
         let mut queue = None;
-        unsafe {
-            do (**self.sem).with |state| {
-                if condvar_id < vec::len(state.blocked) {
-                    // To avoid :broadcast_heavy, we make a new waitqueue,
-                    // swap it out with the old one, and broadcast on the
-                    // old one outside of the little-lock.
-                    queue = Some(util::replace(&mut state.blocked[condvar_id],
-                                               new_waitqueue()));
-                } else {
-                    out_of_bounds = Some(vec::len(state.blocked));
-                }
+        do (**self.sem).with |state| {
+            if condvar_id < vec::len(state.blocked) {
+                // To avoid :broadcast_heavy, we make a new waitqueue,
+                // swap it out with the old one, and broadcast on the
+                // old one outside of the little-lock.
+                queue = Some(util::replace(&mut state.blocked[condvar_id],
+                                           new_waitqueue()));
+            } else {
+                out_of_bounds = Some(vec::len(state.blocked));
             }
         }
         do check_cvar_bounds(out_of_bounds, condvar_id, "cond.signal_on()") {
diff --git a/src/libstd/timer.rs b/src/libstd/timer.rs
index 229d1a07caa..99e772b0c95 100644
--- a/src/libstd/timer.rs
+++ b/src/libstd/timer.rs
@@ -42,47 +42,45 @@ pub fn delayed_send<T:Owned>(iotask: &IoTask,
                               msecs: uint,
                               ch: &Chan<T>,
                               val: T) {
+    let (timer_done_po, timer_done_ch) = stream::<()>();
+    let timer_done_ch = SharedChan(timer_done_ch);
+    let timer = uv::ll::timer_t();
+    let timer_ptr = ptr::addr_of(&timer);
+    do iotask::interact(iotask) |loop_ptr| {
         unsafe {
-            let (timer_done_po, timer_done_ch) = stream::<()>();
-            let timer_done_ch = SharedChan(timer_done_ch);
-            let timer = uv::ll::timer_t();
-            let timer_ptr = ptr::addr_of(&timer);
-            do iotask::interact(iotask) |loop_ptr| {
-                unsafe {
-                    let init_result = uv::ll::timer_init(loop_ptr, timer_ptr);
-                    if (init_result == 0i32) {
-                        let start_result = uv::ll::timer_start(
-                            timer_ptr, delayed_send_cb, msecs, 0u);
-                        if (start_result == 0i32) {
-                            // Note: putting the channel into a ~
-                            // to cast to *c_void
-                            let timer_done_ch_clone = ~timer_done_ch.clone();
-                            let timer_done_ch_ptr = transmute::<
-                                ~SharedChan<()>, *c_void>(
-                                timer_done_ch_clone);
-                            uv::ll::set_data_for_uv_handle(
-                                timer_ptr,
-                                timer_done_ch_ptr);
-                        } else {
-                            let error_msg = uv::ll::get_last_err_info(
-                                loop_ptr);
-                            fail!(~"timer::delayed_send() start failed: " +
-                                error_msg);
-                        }
-                    } else {
-                        let error_msg = uv::ll::get_last_err_info(loop_ptr);
-                        fail!(~"timer::delayed_send() init failed: " +
-                            error_msg);
-                    }
+            let init_result = uv::ll::timer_init(loop_ptr, timer_ptr);
+            if (init_result == 0i32) {
+                let start_result = uv::ll::timer_start(
+                    timer_ptr, delayed_send_cb, msecs, 0u);
+                if (start_result == 0i32) {
+                    // Note: putting the channel into a ~
+                    // to cast to *c_void
+                    let timer_done_ch_clone = ~timer_done_ch.clone();
+                    let timer_done_ch_ptr = transmute::<
+                        ~SharedChan<()>, *c_void>(
+                        timer_done_ch_clone);
+                    uv::ll::set_data_for_uv_handle(
+                        timer_ptr,
+                        timer_done_ch_ptr);
+                } else {
+                    let error_msg = uv::ll::get_last_err_info(
+                        loop_ptr);
+                    fail!(~"timer::delayed_send() start failed: " +
+                        error_msg);
                 }
-            };
-            // delayed_send_cb has been processed by libuv
-            timer_done_po.recv();
-            // notify the caller immediately
-            ch.send(val);
-            // uv_close for this timer has been processed
-            timer_done_po.recv();
+            } else {
+                let error_msg = uv::ll::get_last_err_info(loop_ptr);
+                fail!(~"timer::delayed_send() init failed: " +
+                    error_msg);
+            }
+        }
     };
+    // delayed_send_cb has been processed by libuv
+    timer_done_po.recv();
+    // notify the caller immediately
+    ch.send(val);
+    // uv_close for this timer has been processed
+    timer_done_po.recv();
 }
 
 /**
diff --git a/src/libstd/uv_iotask.rs b/src/libstd/uv_iotask.rs
index fe40fc6a78b..7a9d2438e6a 100644
--- a/src/libstd/uv_iotask.rs
+++ b/src/libstd/uv_iotask.rs
@@ -75,7 +75,7 @@ pub fn spawn_iotask(task: task::TaskBuilder) -> IoTask {
  * module. It is not safe to send the `loop_ptr` param to this callback out
  * via ports/chans.
  */
-pub unsafe fn interact(iotask: &IoTask, cb: ~fn(*c_void)) {
+pub fn interact(iotask: &IoTask, cb: ~fn(*c_void)) {
     send_msg(iotask, Interaction(cb));
 }
 
@@ -87,9 +87,7 @@ pub unsafe fn interact(iotask: &IoTask, cb: ~fn(*c_void)) {
  * closed, causing a failure otherwise.
  */
 pub fn exit(iotask: &IoTask) {
-    unsafe {
-        send_msg(iotask, TeardownLoop);
-    }
+    send_msg(iotask, TeardownLoop);
 }
 
 
diff --git a/src/libstd/uv_ll.rs b/src/libstd/uv_ll.rs
index 7582a7cff51..3bf297027d4 100644
--- a/src/libstd/uv_ll.rs
+++ b/src/libstd/uv_ll.rs
@@ -1111,22 +1111,22 @@ pub unsafe fn freeaddrinfo(res: *addrinfo) {
 }
 
 // libuv struct initializers
-pub unsafe fn tcp_t() -> uv_tcp_t {
+pub fn tcp_t() -> uv_tcp_t {
     return uv_ll_struct_stubgen::gen_stub_uv_tcp_t();
 }
-pub unsafe fn connect_t() -> uv_connect_t {
+pub fn connect_t() -> uv_connect_t {
     return uv_ll_struct_stubgen::gen_stub_uv_connect_t();
 }
-pub unsafe fn write_t() -> uv_write_t {
+pub fn write_t() -> uv_write_t {
     return uv_ll_struct_stubgen::gen_stub_uv_write_t();
 }
-pub unsafe fn async_t() -> uv_async_t {
+pub fn async_t() -> uv_async_t {
     return uv_ll_struct_stubgen::gen_stub_uv_async_t();
 }
-pub unsafe fn timer_t() -> uv_timer_t {
+pub fn timer_t() -> uv_timer_t {
     return uv_ll_struct_stubgen::gen_stub_uv_timer_t();
 }
-pub unsafe fn getaddrinfo_t() -> uv_getaddrinfo_t {
+pub fn getaddrinfo_t() -> uv_getaddrinfo_t {
     return uv_ll_struct_stubgen::gen_stub_uv_getaddrinfo_t();
 }
 
diff --git a/src/libsyntax/abi.rs b/src/libsyntax/abi.rs
index 37fed113382..a848f97d2e0 100644
--- a/src/libsyntax/abi.rs
+++ b/src/libsyntax/abi.rs
@@ -274,13 +274,11 @@ impl ToStr for Abi {
 
 impl ToStr for AbiSet {
     fn to_str(&self) -> ~str {
-        unsafe { // so we can push to strs.
-            let mut strs = ~[];
-            for self.each |abi| {
-                strs.push(abi.data().name);
-            }
-            fmt!("\"%s\"", str::connect_slices(strs, " "))
+        let mut strs = ~[];
+        for self.each |abi| {
+            strs.push(abi.data().name);
         }
+        fmt!("\"%s\"", str::connect_slices(strs, " "))
     }
 }
 
diff --git a/src/test/compile-fail/unused-unsafe.rs b/src/test/compile-fail/unused-unsafe.rs
new file mode 100644
index 00000000000..368a0fbe9be
--- /dev/null
+++ b/src/test/compile-fail/unused-unsafe.rs
@@ -0,0 +1,44 @@
+// Copyright 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.
+
+// Exercise the unused_unsafe attribute in some positive and negative cases
+
+#[deny(unused_unsafe)];
+
+use core::libc;
+
+fn callback<T>(_f: &fn() -> T) -> T { fail!() }
+
+fn bad1() { unsafe {} }                  //~ ERROR: unnecessary "unsafe" block
+fn bad2() { unsafe { bad1() } }          //~ ERROR: unnecessary "unsafe" block
+unsafe fn bad3() {}                      //~ ERROR: unnecessary "unsafe" function
+unsafe fn bad4() { unsafe {} }           //~ ERROR: unnecessary "unsafe" function
+                                         //~^ ERROR: unnecessary "unsafe" block
+fn bad5() { unsafe { do callback {} } }  //~ ERROR: unnecessary "unsafe" block
+
+unsafe fn good0() { libc::exit(1) }
+fn good1() { unsafe { libc::exit(1) } }
+fn good2() {
+    /* bug uncovered when implementing warning about unused unsafe blocks. Be
+       sure that when purity is inherited that the source of the unsafe-ness
+       is tracked correctly */
+    unsafe {
+        unsafe fn what() -> ~[~str] { libc::exit(2) }
+
+        do callback {
+            what();
+        }
+    }
+}
+
+#[allow(unused_unsafe)] unsafe fn allowed0() {}
+#[allow(unused_unsafe)] fn allowed1() { unsafe {} }
+
+fn main() { }