Merge pr/457 into pr/456
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 26b2722..be15d31 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,8 +15,8 @@
         include:
           - rust: nightly
           - rust: beta
-          - rust: stable
-          - rust: 1.43.0
+          #- rust: stable
+          #- rust: 1.48.0
           - name: macOS
             rust: nightly
             os: macos
@@ -51,7 +51,7 @@
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: dtolnay/rust-toolchain@stable
+      - uses: dtolnay/rust-toolchain@beta
       - uses: actions/setup-java@v1
         with:
           java-version: 11
diff --git a/README.md b/README.md
index a962b52..0ae6686 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@
 cxx-build = "0.5"
 ```
 
-*Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+*Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
 *[Release notes](https://github.com/dtolnay/cxx/releases)*
 
 <br>
@@ -90,7 +90,7 @@
         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
     }
 
-    extern "C++" {
+    unsafe extern "C++" {
         // One or more headers with the matching C++ declarations. Our code
         // generators don't read it but it gets #include'd and used in static
         // assertions to ensure our picture of the FFI boundary is accurate.
diff --git a/WORKSPACE b/WORKSPACE
index 6093eee..3fc2f90 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -25,15 +25,17 @@
 load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set")
 
 rust_repository_set(
-    name = "rust_1_47_linux",
+    name = "rust_1_48_beta_linux",
     exec_triple = "x86_64-unknown-linux-gnu",
-    version = "1.47.0",
+    iso_date = "2020-11-08",
+    version = "beta",
 )
 
 rust_repository_set(
-    name = "rust_1_47_darwin",
+    name = "rust_1_48_beta_darwin",
     exec_triple = "x86_64-apple-darwin",
-    version = "1.47.0",
+    iso_date = "2020-11-08",
+    version = "beta",
 )
 
 vendor(
diff --git a/demo/src/main.rs b/demo/src/main.rs
index 10f57e5..dca596a 100644
--- a/demo/src/main.rs
+++ b/demo/src/main.rs
@@ -14,7 +14,7 @@
     }
 
     // C++ types and signatures exposed to Rust.
-    extern "C++" {
+    unsafe extern "C++" {
         include!("demo/include/blobstore.h");
 
         type BlobstoreClient;
diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs
index ebb69a8..d035b52 100644
--- a/gen/lib/tests/test.rs
+++ b/gen/lib/tests/test.rs
@@ -6,7 +6,7 @@
     let rs = quote! {
         #[cxx::bridge]
         mod ffi {
-            extern "C++" {
+            unsafe extern "C++" {
                 fn in_C();
             }
             extern "Rust" {
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 0ae7168..903585e 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -563,14 +563,10 @@
     });
     let vars = receiver_var.into_iter().chain(arg_vars);
 
+    let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke));
+
     let mut call = match invoke {
-        Some(ident) => match &sig.receiver {
-            None => quote!(super::#ident),
-            Some(receiver) => {
-                let receiver_type = &receiver.ty;
-                quote!(#receiver_type::#ident)
-            }
-        },
+        Some(_) => quote!(#local_name),
         None => quote!(::std::mem::transmute::<*const (), #sig>(__extern)),
     };
     call.extend(quote! { (#(#vars),*) });
@@ -648,11 +644,63 @@
         #[export_name = #link_name]
         unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
             let __fn = concat!(module_path!(), #catch_unwind_label);
+            #wrap_super
             #expr
         }
     }
 }
 
+// A wrapper like `fn f(x: Arg) { super::f(x) }` just to ensure we have the
+// accurate unsafety declaration and no problematic elided lifetimes.
+fn expand_rust_function_shim_super(
+    sig: &Signature,
+    local_name: &Ident,
+    invoke: &Ident,
+) -> TokenStream {
+    let unsafety = sig.unsafety;
+
+    let receiver_var = sig
+        .receiver
+        .as_ref()
+        .map(|receiver| Ident::new("__self", receiver.var.span));
+    let receiver = sig.receiver.iter().map(|receiver| {
+        let receiver_type = receiver.ty();
+        quote!(#receiver_var: #receiver_type)
+    });
+    let args = sig.args.iter().map(|arg| quote!(#arg));
+    let all_args = receiver.chain(args);
+
+    let ret = if let Some((result, _langle, rangle)) = sig.throws_tokens {
+        let ok = match &sig.ret {
+            Some(ret) => quote!(#ret),
+            None => quote!(()),
+        };
+        let impl_trait = quote_spanned!(result.span=> impl);
+        let display = quote_spanned!(rangle.span=> ::std::fmt::Display);
+        quote!(-> ::std::result::Result<#ok, #impl_trait #display>)
+    } else {
+        expand_return_type(&sig.ret)
+    };
+
+    let arg_vars = sig.args.iter().map(|arg| &arg.ident);
+    let vars = receiver_var.iter().chain(arg_vars);
+
+    let span = invoke.span();
+    let call = match &sig.receiver {
+        None => quote_spanned!(span=> super::#invoke),
+        Some(receiver) => {
+            let receiver_type = &receiver.ty;
+            quote_spanned!(span=> #receiver_type::#invoke)
+        }
+    };
+
+    quote_spanned! {span=>
+        #unsafety fn #local_name(#(#all_args,)*) #ret {
+            #call(#(#vars,)*)
+        }
+    }
+}
+
 fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
     let doc = &alias.doc;
     let ident = &alias.name.rust;
diff --git a/src/extern_type.rs b/src/extern_type.rs
index 008fdc1..d7634c4 100644
--- a/src/extern_type.rs
+++ b/src/extern_type.rs
@@ -30,7 +30,7 @@
 /// # mod file1 {
 /// #[cxx::bridge(namespace = "example")]
 /// pub mod ffi {
-///     extern "C++" {
+///     unsafe extern "C++" {
 ///         type Demo;
 ///
 ///         fn create_demo() -> UniquePtr<Demo>;
@@ -41,7 +41,7 @@
 /// // file2.rs
 /// #[cxx::bridge(namespace = "example")]
 /// pub mod ffi {
-///     extern "C++" {
+///     unsafe extern "C++" {
 ///         type Demo = crate::file1::ffi::Demo;
 ///
 ///         fn take_ref_demo(demo: &Demo);
@@ -80,7 +80,7 @@
 ///
 /// #[cxx::bridge(namespace = "folly")]
 /// pub mod ffi {
-///     extern "C++" {
+///     unsafe extern "C++" {
 ///         include!("rust_cxx_bindings.h");
 ///
 ///         type StringPiece = crate::folly_sys::StringPiece;
diff --git a/src/lib.rs b/src/lib.rs
index c72e362..04266fb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,7 +18,7 @@
 //!
 //! <br>
 //!
-//! *Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+//! *Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
 //! *[Release notes](https://github.com/dtolnay/cxx/releases)*
 //!
 //! <br>
@@ -86,7 +86,7 @@
 //!         fn next_chunk(buf: &mut MultiBuf) -> &[u8];
 //!     }
 //!
-//!     extern "C++" {
+//!     unsafe extern "C++" {
 //!         // One or more headers with the matching C++ declarations. Our code
 //!         // generators don't read it but it gets #include'd and used in static
 //!         // assertions to ensure our picture of the FFI boundary is accurate.
diff --git a/syntax/parse.rs b/syntax/parse.rs
index b557584..9509bd8 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -204,7 +204,7 @@
         Lang::Rust => {
             if foreign_mod.unsafety.is_some() {
                 let unsafety = foreign_mod.unsafety;
-                let abi = foreign_mod.abi;
+                let abi = &foreign_mod.abi;
                 let span = quote!(#unsafety #abi);
                 cx.error(span, "extern \"Rust\" block does not need to be unsafe");
             }
@@ -253,6 +253,18 @@
         }
     }
 
+    if !trusted
+        && items.iter().any(|api| match api {
+            Api::CxxFunction(efn) => efn.unsafety.is_none(),
+            _ => false,
+        })
+    {
+        cx.error(
+            foreign_mod.abi,
+            "block must be declared `unsafe extern \"C++\"` if it contains any safe-to-call C++ functions",
+        );
+    }
+
     let mut types = items.iter().filter_map(|item| match item {
         Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name),
         Api::TypeAlias(alias) => Some(&alias.name),
diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs
index 340e2d7..a0c9ff4 100644
--- a/tests/cxx_gen.rs
+++ b/tests/cxx_gen.rs
@@ -6,7 +6,7 @@
 const BRIDGE0: &str = r#"
     #[cxx::bridge]
     mod ffi {
-        extern "C++" {
+        unsafe extern "C++" {
             pub fn do_cpp_thing(foo: &str);
         }
     }
diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs
index b5ed579..8ca5e57 100644
--- a/tests/ffi/extra.rs
+++ b/tests/ffi/extra.rs
@@ -15,7 +15,7 @@
     impl UniquePtr<F> {}
     impl UniquePtr<G> {}
 
-    extern "C++" {
+    unsafe extern "C++" {
         include!("tests/ffi/tests.h");
 
         type D = crate::other::D;
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 850a2af..ed6740d 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -1,5 +1,6 @@
 #![allow(
     clippy::boxed_local,
+    clippy::just_underscores_and_digits,
     clippy::ptr_arg,
     clippy::trivially_copy_pass_by_ref
 )]
@@ -115,7 +116,7 @@
         i: i32,
     }
 
-    extern "C++" {
+    unsafe extern "C++" {
         include!("tests/ffi/tests.h");
 
         type C;
diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs
index cf53cde..aff1b1c 100644
--- a/tests/ffi/module.rs
+++ b/tests/ffi/module.rs
@@ -3,7 +3,7 @@
 #[rustfmt::skip]
 #[cxx::bridge(namespace = "tests")]
 pub mod ffi {
-    extern "C++" {
+    unsafe extern "C++" {
         include!("tests/ffi/tests.h");
 
         type C = crate::ffi::C;
diff --git a/tests/ui/disallow_lifetime.rs b/tests/ui/disallow_lifetime.rs
index b07697a..a4a7b51 100644
--- a/tests/ui/disallow_lifetime.rs
+++ b/tests/ui/disallow_lifetime.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C++" {
+    unsafe extern "C++" {
         type C;
         fn f(&'static self);
     }
diff --git a/tests/ui/missing_unsafe.rs b/tests/ui/missing_unsafe.rs
new file mode 100644
index 0000000..d8c0a23
--- /dev/null
+++ b/tests/ui/missing_unsafe.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn f(x: i32);
+    }
+}
+
+unsafe fn f(_x: i32) {}
+
+fn main() {}
diff --git a/tests/ui/missing_unsafe.stderr b/tests/ui/missing_unsafe.stderr
new file mode 100644
index 0000000..df1bce2
--- /dev/null
+++ b/tests/ui/missing_unsafe.stderr
@@ -0,0 +1,7 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+ --> $DIR/missing_unsafe.rs:4:12
+  |
+4 |         fn f(x: i32);
+  |            ^ call to unsafe function
+  |
+  = note: consult the function's documentation for information on how to avoid undefined behavior
diff --git a/tests/ui/reference_to_reference.rs b/tests/ui/reference_to_reference.rs
index 2318533..91fe160 100644
--- a/tests/ui/reference_to_reference.rs
+++ b/tests/ui/reference_to_reference.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C++" {
+    unsafe extern "C++" {
         type ThingC;
         fn repro_c(t: &&ThingC);
     }
diff --git a/tests/ui/result_no_display.rs b/tests/ui/result_no_display.rs
new file mode 100644
index 0000000..b535677
--- /dev/null
+++ b/tests/ui/result_no_display.rs
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        fn f() -> Result<()>;
+    }
+}
+
+pub struct NonError;
+
+fn f() -> Result<(), NonError> {
+    Ok(())
+}
+
+fn main() {}
diff --git a/tests/ui/result_no_display.stderr b/tests/ui/result_no_display.stderr
new file mode 100644
index 0000000..d0502f9
--- /dev/null
+++ b/tests/ui/result_no_display.stderr
@@ -0,0 +1,8 @@
+error[E0277]: `NonError` doesn't implement `std::fmt::Display`
+ --> $DIR/result_no_display.rs:4:19
+  |
+4 |         fn f() -> Result<()>;
+  |                   ^^^^^^^^^^ `NonError` cannot be formatted with the default formatter
+  |
+  = help: the trait `std::fmt::Display` is not implemented for `NonError`
+  = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/unnamed_receiver.rs b/tests/ui/unnamed_receiver.rs
index 13c23ae..5f53a0a 100644
--- a/tests/ui/unnamed_receiver.rs
+++ b/tests/ui/unnamed_receiver.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C++" {
+    unsafe extern "C++" {
         type One;
         type Two;
         fn f(&mut self);
diff --git a/tests/ui/unrecognized_receiver.rs b/tests/ui/unrecognized_receiver.rs
index 5b09c56..eee8259 100644
--- a/tests/ui/unrecognized_receiver.rs
+++ b/tests/ui/unrecognized_receiver.rs
@@ -1,6 +1,6 @@
 #[cxx::bridge]
 mod ffi {
-    extern "C++" {
+    unsafe extern "C++" {
         fn f(self: &Unrecognized);
     }
 }
diff --git a/tests/ui/unsupported_elided.rs b/tests/ui/unsupported_elided.rs
new file mode 100644
index 0000000..4033319
--- /dev/null
+++ b/tests/ui/unsupported_elided.rs
@@ -0,0 +1,20 @@
+use std::marker::PhantomData;
+
+#[cxx::bridge]
+mod ffi {
+    extern "Rust" {
+        type T;
+
+        fn f(t: &T) -> &str;
+    }
+}
+
+pub struct T<'a> {
+    _lifetime: PhantomData<&'a ()>,
+}
+
+fn f<'a>(_t: &T<'a>) -> &'a str {
+    ""
+}
+
+fn main() {}
diff --git a/tests/ui/unsupported_elided.stderr b/tests/ui/unsupported_elided.stderr
new file mode 100644
index 0000000..5136390
--- /dev/null
+++ b/tests/ui/unsupported_elided.stderr
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+ --> $DIR/unsupported_elided.rs:8:24
+  |
+8 |         fn f(t: &T) -> &str;
+  |                 --     ^ expected named lifetime parameter
+  |
+  = help: this function's return type contains a borrowed value, but the signature does not say which one of `t`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+  |
+8 |         fn f<'a>(t: &'a T) -> &'a str;
+  |             ^^^^    ^^^^^     ^^^