about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_lint/types.rs113
-rw-r--r--src/test/ui/issues/issue-14309.rs2
-rw-r--r--src/test/ui/issues/issue-14309.stderr25
-rw-r--r--src/test/ui/issues/issue-16250.rs2
-rw-r--r--src/test/ui/issues/issue-16250.stderr5
-rw-r--r--src/test/ui/lint/lint-ctypes-enum.rs21
-rw-r--r--src/test/ui/lint/lint-ctypes-enum.stderr57
-rw-r--r--src/test/ui/lint/lint-ctypes.rs13
-rw-r--r--src/test/ui/lint/lint-ctypes.stderr127
-rw-r--r--src/test/ui/lint/opaque-ty-ffi-unsafe.rs16
-rw-r--r--src/test/ui/lint/opaque-ty-ffi-unsafe.stderr15
-rw-r--r--src/test/ui/union/union-repr-c.rs2
-rw-r--r--src/test/ui/union/union-repr-c.stderr5
13 files changed, 268 insertions, 135 deletions
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 217e10ab24f..3b8c06ba154 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -624,7 +624,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                     AdtKind::Struct => {
                         if !def.repr.c() && !def.repr.transparent() {
                             return FfiUnsafe {
-                                ty: ty,
+                                ty,
                                 reason: "this struct has unspecified layout",
                                 help: Some("consider adding a `#[repr(C)]` or \
                                             `#[repr(transparent)]` attribute to this struct"),
@@ -633,7 +633,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
                         if def.non_enum_variant().fields.is_empty() {
                             return FfiUnsafe {
-                                ty: ty,
+                                ty,
                                 reason: "this struct has no fields",
                                 help: Some("consider adding a member to this struct"),
                             };
@@ -669,7 +669,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                     AdtKind::Union => {
                         if !def.repr.c() && !def.repr.transparent() {
                             return FfiUnsafe {
-                                ty: ty,
+                                ty,
                                 reason: "this union has unspecified layout",
                                 help: Some("consider adding a `#[repr(C)]` or \
                                             `#[repr(transparent)]` attribute to this union"),
@@ -678,7 +678,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
                         if def.non_enum_variant().fields.is_empty() {
                             return FfiUnsafe {
-                                ty: ty,
+                                ty,
                                 reason: "this union has no fields",
                                 help: Some("consider adding a field to this union"),
                             };
@@ -721,7 +721,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                             // Special-case types like `Option<extern fn()>`.
                             if !is_repr_nullable_ptr(cx, ty, def, substs) {
                                 return FfiUnsafe {
-                                    ty: ty,
+                                    ty,
                                     reason: "enum has no representation hint",
                                     help: Some("consider adding a `#[repr(C)]`, \
                                                 `#[repr(transparent)]`, or integer `#[repr(...)]` \
@@ -750,7 +750,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                     }
                                     FfiPhantom(..) => {
                                         return FfiUnsafe {
-                                            ty: ty,
+                                            ty,
                                             reason: "this enum contains a PhantomData field",
                                             help: None,
                                         };
@@ -764,13 +764,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             }
 
             ty::Char => FfiUnsafe {
-                ty: ty,
+                ty,
                 reason: "the `char` type has no C equivalent",
                 help: Some("consider using `u32` or `libc::wchar_t` instead"),
             },
 
             ty::Int(ast::IntTy::I128) | ty::Uint(ast::UintTy::U128) => FfiUnsafe {
-                ty: ty,
+                ty,
                 reason: "128-bit integers don't currently have a known stable ABI",
                 help: None,
             },
@@ -779,25 +779,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
 
             ty::Slice(_) => FfiUnsafe {
-                ty: ty,
+                ty,
                 reason: "slices have no C equivalent",
                 help: Some("consider using a raw pointer instead"),
             },
 
             ty::Dynamic(..) => FfiUnsafe {
-                ty: ty,
+                ty,
                 reason: "trait objects have no C equivalent",
                 help: None,
             },
 
             ty::Str => FfiUnsafe {
-                ty: ty,
+                ty,
                 reason: "string slices have no C equivalent",
                 help: Some("consider using `*const u8` and a length instead"),
             },
 
             ty::Tuple(..) => FfiUnsafe {
-                ty: ty,
+                ty,
                 reason: "tuples have unspecified layout",
                 help: Some("consider using a struct instead"),
             },
@@ -811,7 +811,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 match sig.abi() {
                     Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
                         return FfiUnsafe {
-                            ty: ty,
+                            ty,
                             reason: "this function pointer has Rust-specific calling convention",
                             help: Some("consider using an `extern fn(...) -> ...` \
                                         function pointer instead"),
@@ -855,11 +855,76 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             ty::UnnormalizedProjection(..) |
             ty::Projection(..) |
             ty::Opaque(..) |
-            ty::FnDef(..) => bug!("Unexpected type in foreign function"),
+            ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
+        }
+    }
+
+    fn emit_ffi_unsafe_type_lint(
+        &mut self,
+        ty: Ty<'tcx>,
+        sp: Span,
+        note: &str,
+        help: Option<&str>,
+    ) {
+        let mut diag = self.cx.struct_span_lint(
+            IMPROPER_CTYPES,
+            sp,
+            &format!("`extern` block uses type `{}`, which is not FFI-safe", ty),
+        );
+        diag.span_label(sp, "not FFI-safe");
+        if let Some(help) = help {
+            diag.help(help);
+        }
+        diag.note(note);
+        if let ty::Adt(def, _) = ty.sty {
+            if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
+                diag.span_note(sp, "type defined here");
+            }
+        }
+        diag.emit();
+    }
+
+    fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
+        use crate::rustc::ty::TypeFoldable;
+
+        struct ProhibitOpaqueTypes<'tcx> {
+            ty: Option<Ty<'tcx>>,
+        };
+
+        impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'tcx> {
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
+                if let ty::Opaque(..) = ty.sty {
+                    self.ty = Some(ty);
+                    true
+                } else {
+                    ty.super_visit_with(self)
+                }
+            }
+        }
+
+        let mut visitor = ProhibitOpaqueTypes { ty: None };
+        ty.visit_with(&mut visitor);
+        if let Some(ty) = visitor.ty {
+            self.emit_ffi_unsafe_type_lint(
+                ty,
+                sp,
+                "opaque types have no C equivalent",
+                None,
+            );
+            true
+        } else {
+            false
         }
     }
 
     fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) {
+        // We have to check for opaque types before `normalize_erasing_regions`,
+        // which will replace opaque types with their underlying concrete type.
+        if self.check_for_opaque_ty(sp, ty) {
+            // We've already emitted an error due to an opaque type.
+            return;
+        }
+
         // it is only OK to use this function because extern fns cannot have
         // any generic types right now:
         let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
@@ -867,24 +932,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
             FfiResult::FfiSafe => {}
             FfiResult::FfiPhantom(ty) => {
-                self.cx.span_lint(IMPROPER_CTYPES,
-                                  sp,
-                                  &format!("`extern` block uses type `{}` which is not FFI-safe: \
-                                            composed only of PhantomData", ty));
+                self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None);
             }
-            FfiResult::FfiUnsafe { ty: unsafe_ty, reason, help } => {
-                let msg = format!("`extern` block uses type `{}` which is not FFI-safe: {}",
-                                  unsafe_ty, reason);
-                let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
-                if let Some(s) = help {
-                    diag.help(s);
-                }
-                if let ty::Adt(def, _) = unsafe_ty.sty {
-                    if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
-                        diag.span_note(sp, "type defined here");
-                    }
-                }
-                diag.emit();
+            FfiResult::FfiUnsafe { ty, reason, help } => {
+                self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
             }
         }
     }
diff --git a/src/test/ui/issues/issue-14309.rs b/src/test/ui/issues/issue-14309.rs
index d0e532a2646..328a4c982b8 100644
--- a/src/test/ui/issues/issue-14309.rs
+++ b/src/test/ui/issues/issue-14309.rs
@@ -27,7 +27,7 @@ struct D {
 }
 
 extern "C" {
-    fn foo(x: A); //~ ERROR type `A` which is not FFI-safe
+    fn foo(x: A); //~ ERROR type `A`, which is not FFI-safe
     fn bar(x: B); //~ ERROR type `A`
     fn baz(x: C);
     fn qux(x: A2); //~ ERROR type `A`
diff --git a/src/test/ui/issues/issue-14309.stderr b/src/test/ui/issues/issue-14309.stderr
index e0491093a72..f598e1f9e2f 100644
--- a/src/test/ui/issues/issue-14309.stderr
+++ b/src/test/ui/issues/issue-14309.stderr
@@ -1,8 +1,8 @@
-error: `extern` block uses type `A` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `A`, which is not FFI-safe
   --> $DIR/issue-14309.rs:30:15
    |
 LL |     fn foo(x: A);
-   |               ^
+   |               ^ not FFI-safe
    |
 note: lint level defined here
   --> $DIR/issue-14309.rs:1:9
@@ -10,6 +10,7 @@ note: lint level defined here
 LL | #![deny(improper_ctypes)]
    |         ^^^^^^^^^^^^^^^
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/issue-14309.rs:4:1
    |
@@ -18,13 +19,14 @@ LL | |     x: i32
 LL | | }
    | |_^
 
-error: `extern` block uses type `A` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `A`, which is not FFI-safe
   --> $DIR/issue-14309.rs:31:15
    |
 LL |     fn bar(x: B);
-   |               ^
+   |               ^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/issue-14309.rs:4:1
    |
@@ -33,13 +35,14 @@ LL | |     x: i32
 LL | | }
    | |_^
 
-error: `extern` block uses type `A` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `A`, which is not FFI-safe
   --> $DIR/issue-14309.rs:33:15
    |
 LL |     fn qux(x: A2);
-   |               ^^
+   |               ^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/issue-14309.rs:4:1
    |
@@ -48,13 +51,14 @@ LL | |     x: i32
 LL | | }
    | |_^
 
-error: `extern` block uses type `A` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `A`, which is not FFI-safe
   --> $DIR/issue-14309.rs:34:16
    |
 LL |     fn quux(x: B2);
-   |                ^^
+   |                ^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/issue-14309.rs:4:1
    |
@@ -63,13 +67,14 @@ LL | |     x: i32
 LL | | }
    | |_^
 
-error: `extern` block uses type `A` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `A`, which is not FFI-safe
   --> $DIR/issue-14309.rs:36:16
    |
 LL |     fn fred(x: D);
-   |                ^
+   |                ^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/issue-14309.rs:4:1
    |
diff --git a/src/test/ui/issues/issue-16250.rs b/src/test/ui/issues/issue-16250.rs
index bf01627adfc..a3c6751ad89 100644
--- a/src/test/ui/issues/issue-16250.rs
+++ b/src/test/ui/issues/issue-16250.rs
@@ -3,7 +3,7 @@
 pub struct Foo;
 
 extern {
-    pub fn foo(x: (Foo)); //~ ERROR unspecified layout
+    pub fn foo(x: (Foo)); //~ ERROR `extern` block uses type `Foo`
 }
 
 fn main() {
diff --git a/src/test/ui/issues/issue-16250.stderr b/src/test/ui/issues/issue-16250.stderr
index f3686e82b05..5686ac37742 100644
--- a/src/test/ui/issues/issue-16250.stderr
+++ b/src/test/ui/issues/issue-16250.stderr
@@ -1,8 +1,8 @@
-error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `Foo`, which is not FFI-safe
   --> $DIR/issue-16250.rs:6:20
    |
 LL |     pub fn foo(x: (Foo));
-   |                    ^^^
+   |                    ^^^ not FFI-safe
    |
 note: lint level defined here
   --> $DIR/issue-16250.rs:1:9
@@ -11,6 +11,7 @@ LL | #![deny(warnings)]
    |         ^^^^^^^^
    = note: `#[deny(improper_ctypes)]` implied by `#[deny(warnings)]`
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/issue-16250.rs:3:1
    |
diff --git a/src/test/ui/lint/lint-ctypes-enum.rs b/src/test/ui/lint/lint-ctypes-enum.rs
index e1f4b0b34eb..3898e67a07f 100644
--- a/src/test/ui/lint/lint-ctypes-enum.rs
+++ b/src/test/ui/lint/lint-ctypes-enum.rs
@@ -36,36 +36,37 @@ struct Rust<T>(T);
 
 extern {
    fn zf(x: Z);
-   fn uf(x: U); //~ ERROR enum has no representation hint
-   fn bf(x: B); //~ ERROR enum has no representation hint
-   fn tf(x: T); //~ ERROR enum has no representation hint
+   fn uf(x: U); //~ ERROR `extern` block uses type `U`
+   fn bf(x: B); //~ ERROR `extern` block uses type `B`
+   fn tf(x: T); //~ ERROR `extern` block uses type `T`
    fn repr_c(x: ReprC);
    fn repr_u8(x: U8);
    fn repr_isize(x: Isize);
    fn option_ref(x: Option<&'static u8>);
    fn option_fn(x: Option<extern "C" fn()>);
    fn nonnull(x: Option<std::ptr::NonNull<u8>>);
-   fn unique(x: Option<std::ptr::Unique<u8>>); //~ ERROR enum has no representation hint
+   fn unique(x: Option<std::ptr::Unique<u8>>);
+   //~^ ERROR `extern` block uses type `std::option::Option<std::ptr::Unique<u8>>`
    fn nonzero_u8(x: Option<num::NonZeroU8>);
    fn nonzero_u16(x: Option<num::NonZeroU16>);
    fn nonzero_u32(x: Option<num::NonZeroU32>);
    fn nonzero_u64(x: Option<num::NonZeroU64>);
    fn nonzero_u128(x: Option<num::NonZeroU128>);
-   //~^ ERROR 128-bit integers don't currently have a known stable ABI
+   //~^ ERROR `extern` block uses type `u128`
    fn nonzero_usize(x: Option<num::NonZeroUsize>);
    fn nonzero_i8(x: Option<num::NonZeroI8>);
    fn nonzero_i16(x: Option<num::NonZeroI16>);
    fn nonzero_i32(x: Option<num::NonZeroI32>);
    fn nonzero_i64(x: Option<num::NonZeroI64>);
    fn nonzero_i128(x: Option<num::NonZeroI128>);
-   //~^ ERROR 128-bit integers don't currently have a known stable ABI
+   //~^ ERROR `extern` block uses type `i128`
    fn nonzero_isize(x: Option<num::NonZeroIsize>);
    fn transparent_struct(x: Option<TransparentStruct<num::NonZeroU8>>);
    fn transparent_enum(x: Option<TransparentEnum<num::NonZeroU8>>);
    fn transparent_union(x: Option<TransparentUnion<num::NonZeroU8>>);
-   //~^ ERROR enum has no representation hint
-   fn repr_rust(x: Option<Rust<num::NonZeroU8>>); //~ ERROR enum has no representation hint
-   fn no_result(x: Result<(), num::NonZeroI32>); //~ ERROR enum has no representation hint
+   //~^ ERROR `extern` block uses type
+   fn repr_rust(x: Option<Rust<num::NonZeroU8>>); //~ ERROR `extern` block uses type
+   fn no_result(x: Result<(), num::NonZeroI32>); //~ ERROR `extern` block uses type
 }
 
-pub fn main() { }
+pub fn main() {}
diff --git a/src/test/ui/lint/lint-ctypes-enum.stderr b/src/test/ui/lint/lint-ctypes-enum.stderr
index 20e43860664..81939e6ee20 100644
--- a/src/test/ui/lint/lint-ctypes-enum.stderr
+++ b/src/test/ui/lint/lint-ctypes-enum.stderr
@@ -1,8 +1,8 @@
-error: `extern` block uses type `U` which is not FFI-safe: enum has no representation hint
+error: `extern` block uses type `U`, which is not FFI-safe
   --> $DIR/lint-ctypes-enum.rs:39:13
    |
 LL |    fn uf(x: U);
-   |             ^
+   |             ^ not FFI-safe
    |
 note: lint level defined here
   --> $DIR/lint-ctypes-enum.rs:3:9
@@ -10,81 +10,92 @@ note: lint level defined here
 LL | #![deny(improper_ctypes)]
    |         ^^^^^^^^^^^^^^^
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
 note: type defined here
   --> $DIR/lint-ctypes-enum.rs:9:1
    |
 LL | enum U { A }
    | ^^^^^^^^^^^^
 
-error: `extern` block uses type `B` which is not FFI-safe: enum has no representation hint
+error: `extern` block uses type `B`, which is not FFI-safe
   --> $DIR/lint-ctypes-enum.rs:40:13
    |
 LL |    fn bf(x: B);
-   |             ^
+   |             ^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
 note: type defined here
   --> $DIR/lint-ctypes-enum.rs:10:1
    |
 LL | enum B { C, D }
    | ^^^^^^^^^^^^^^^
 
-error: `extern` block uses type `T` which is not FFI-safe: enum has no representation hint
+error: `extern` block uses type `T`, which is not FFI-safe
   --> $DIR/lint-ctypes-enum.rs:41:13
    |
 LL |    fn tf(x: T);
-   |             ^
+   |             ^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
 note: type defined here
   --> $DIR/lint-ctypes-enum.rs:11:1
    |
 LL | enum T { E, F, G }
    | ^^^^^^^^^^^^^^^^^^
 
-error: `extern` block uses type `std::option::Option<std::ptr::Unique<u8>>` which is not FFI-safe: enum has no representation hint
+error: `extern` block uses type `std::option::Option<std::ptr::Unique<u8>>`, which is not FFI-safe
   --> $DIR/lint-ctypes-enum.rs:48:17
    |
 LL |    fn unique(x: Option<std::ptr::Unique<u8>>);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
 
-error: `extern` block uses type `u128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
-  --> $DIR/lint-ctypes-enum.rs:53:23
+error: `extern` block uses type `u128`, which is not FFI-safe
+  --> $DIR/lint-ctypes-enum.rs:54:23
    |
 LL |    fn nonzero_u128(x: Option<num::NonZeroU128>);
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: 128-bit integers don't currently have a known stable ABI
 
-error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
-  --> $DIR/lint-ctypes-enum.rs:60:23
+error: `extern` block uses type `i128`, which is not FFI-safe
+  --> $DIR/lint-ctypes-enum.rs:61:23
    |
 LL |    fn nonzero_i128(x: Option<num::NonZeroI128>);
-   |                       ^^^^^^^^^^^^^^^^^^^^^^^^
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: 128-bit integers don't currently have a known stable ABI
 
-error: `extern` block uses type `std::option::Option<TransparentUnion<std::num::NonZeroU8>>` which is not FFI-safe: enum has no representation hint
-  --> $DIR/lint-ctypes-enum.rs:65:28
+error: `extern` block uses type `std::option::Option<TransparentUnion<std::num::NonZeroU8>>`, which is not FFI-safe
+  --> $DIR/lint-ctypes-enum.rs:66:28
    |
 LL |    fn transparent_union(x: Option<TransparentUnion<num::NonZeroU8>>);
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
 
-error: `extern` block uses type `std::option::Option<Rust<std::num::NonZeroU8>>` which is not FFI-safe: enum has no representation hint
-  --> $DIR/lint-ctypes-enum.rs:67:20
+error: `extern` block uses type `std::option::Option<Rust<std::num::NonZeroU8>>`, which is not FFI-safe
+  --> $DIR/lint-ctypes-enum.rs:68:20
    |
 LL |    fn repr_rust(x: Option<Rust<num::NonZeroU8>>);
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
 
-error: `extern` block uses type `std::result::Result<(), std::num::NonZeroI32>` which is not FFI-safe: enum has no representation hint
-  --> $DIR/lint-ctypes-enum.rs:68:20
+error: `extern` block uses type `std::result::Result<(), std::num::NonZeroI32>`, which is not FFI-safe
+  --> $DIR/lint-ctypes-enum.rs:69:20
    |
 LL |    fn no_result(x: Result<(), num::NonZeroI32>);
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
 
 error: aborting due to 9 previous errors
 
diff --git a/src/test/ui/lint/lint-ctypes.rs b/src/test/ui/lint/lint-ctypes.rs
index a3d9b6febdb..e20503a395c 100644
--- a/src/test/ui/lint/lint-ctypes.rs
+++ b/src/test/ui/lint/lint-ctypes.rs
@@ -1,7 +1,7 @@
-#![deny(improper_ctypes)]
 #![feature(rustc_private)]
 
 #![allow(private_in_public)]
+#![deny(improper_ctypes)]
 
 extern crate libc;
 
@@ -54,12 +54,13 @@ extern {
     pub fn trait_type(p: &dyn Clone); //~ ERROR uses type `dyn std::clone::Clone`
     pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
     pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
-    pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields
-    pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData
+    pub fn zero_size(p: ZeroSize); //~ ERROR uses type `ZeroSize`
+    pub fn zero_size_phantom(p: ZeroSizeWithPhantomData);
+    //~^ ERROR uses type `ZeroSizeWithPhantomData`
     pub fn zero_size_phantom_toplevel()
-        -> ::std::marker::PhantomData<bool>; //~ ERROR: composed only of PhantomData
-    pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific
-    pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific
+        -> ::std::marker::PhantomData<bool>; //~ ERROR uses type `std::marker::PhantomData<bool>`
+    pub fn fn_type(p: RustFn); //~ ERROR uses type `fn()`
+    pub fn fn_type2(p: fn()); //~ ERROR uses type `fn()`
     pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box<u32>`
     pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128`
     pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
diff --git a/src/test/ui/lint/lint-ctypes.stderr b/src/test/ui/lint/lint-ctypes.stderr
index c78463beb65..e533a767b31 100644
--- a/src/test/ui/lint/lint-ctypes.stderr
+++ b/src/test/ui/lint/lint-ctypes.stderr
@@ -1,170 +1,201 @@
-error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `Foo`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:46:28
    |
 LL |     pub fn ptr_type1(size: *const Foo);
-   |                            ^^^^^^^^^^
+   |                            ^^^^^^^^^^ not FFI-safe
    |
 note: lint level defined here
-  --> $DIR/lint-ctypes.rs:1:9
+  --> $DIR/lint-ctypes.rs:4:9
    |
 LL | #![deny(improper_ctypes)]
    |         ^^^^^^^^^^^^^^^
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/lint-ctypes.rs:24:1
    |
 LL | pub struct Foo;
    | ^^^^^^^^^^^^^^^
 
-error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `Foo`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:47:28
    |
 LL |     pub fn ptr_type2(size: *const Foo);
-   |                            ^^^^^^^^^^
+   |                            ^^^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 note: type defined here
   --> $DIR/lint-ctypes.rs:24:1
    |
 LL | pub struct Foo;
    | ^^^^^^^^^^^^^^^
 
-error: `extern` block uses type `[u32]` which is not FFI-safe: slices have no C equivalent
+error: `extern` block uses type `[u32]`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:48:26
    |
 LL |     pub fn slice_type(p: &[u32]);
-   |                          ^^^^^^
+   |                          ^^^^^^ not FFI-safe
    |
    = help: consider using a raw pointer instead
+   = note: slices have no C equivalent
 
-error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
+error: `extern` block uses type `str`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:49:24
    |
 LL |     pub fn str_type(p: &str);
-   |                        ^^^^
+   |                        ^^^^ not FFI-safe
    |
    = help: consider using `*const u8` and a length instead
+   = note: string slices have no C equivalent
 
-error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
+error: `extern` block uses type `std::boxed::Box<u32>`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:50:24
    |
 LL |     pub fn box_type(p: Box<u32>);
-   |                        ^^^^^^^^
+   |                        ^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 
-error: `extern` block uses type `char` which is not FFI-safe: the `char` type has no C equivalent
+error: `extern` block uses type `char`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:51:25
    |
 LL |     pub fn char_type(p: char);
-   |                         ^^^^
+   |                         ^^^^ not FFI-safe
    |
    = help: consider using `u32` or `libc::wchar_t` instead
+   = note: the `char` type has no C equivalent
 
-error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+error: `extern` block uses type `i128`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:52:25
    |
 LL |     pub fn i128_type(p: i128);
-   |                         ^^^^
+   |                         ^^^^ not FFI-safe
+   |
+   = note: 128-bit integers don't currently have a known stable ABI
 
-error: `extern` block uses type `u128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+error: `extern` block uses type `u128`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:53:25
    |
 LL |     pub fn u128_type(p: u128);
-   |                         ^^^^
+   |                         ^^^^ not FFI-safe
+   |
+   = note: 128-bit integers don't currently have a known stable ABI
 
-error: `extern` block uses type `dyn std::clone::Clone` which is not FFI-safe: trait objects have no C equivalent
+error: `extern` block uses type `dyn std::clone::Clone`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:54:26
    |
 LL |     pub fn trait_type(p: &dyn Clone);
-   |                          ^^^^^^^^^^
+   |                          ^^^^^^^^^^ not FFI-safe
+   |
+   = note: trait objects have no C equivalent
 
-error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
+error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:55:26
    |
 LL |     pub fn tuple_type(p: (i32, i32));
-   |                          ^^^^^^^^^^
+   |                          ^^^^^^^^^^ not FFI-safe
    |
    = help: consider using a struct instead
+   = note: tuples have unspecified layout
 
-error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
+error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:56:27
    |
 LL |     pub fn tuple_type2(p: I32Pair);
-   |                           ^^^^^^^
+   |                           ^^^^^^^ not FFI-safe
    |
    = help: consider using a struct instead
+   = note: tuples have unspecified layout
 
-error: `extern` block uses type `ZeroSize` which is not FFI-safe: this struct has no fields
+error: `extern` block uses type `ZeroSize`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:57:25
    |
 LL |     pub fn zero_size(p: ZeroSize);
-   |                         ^^^^^^^^
+   |                         ^^^^^^^^ not FFI-safe
    |
    = help: consider adding a member to this struct
+   = note: this struct has no fields
 note: type defined here
   --> $DIR/lint-ctypes.rs:20:1
    |
 LL | pub struct ZeroSize;
    | ^^^^^^^^^^^^^^^^^^^^
 
-error: `extern` block uses type `ZeroSizeWithPhantomData` which is not FFI-safe: composed only of PhantomData
+error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe
   --> $DIR/lint-ctypes.rs:58:33
    |
 LL |     pub fn zero_size_phantom(p: ZeroSizeWithPhantomData);
-   |                                 ^^^^^^^^^^^^^^^^^^^^^^^
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: composed only of `PhantomData`
+note: type defined here
+  --> $DIR/lint-ctypes.rs:43:1
+   |
+LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `extern` block uses type `std::marker::PhantomData<bool>` which is not FFI-safe: composed only of PhantomData
-  --> $DIR/lint-ctypes.rs:60:12
+error: `extern` block uses type `std::marker::PhantomData<bool>`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:61:12
    |
 LL |         -> ::std::marker::PhantomData<bool>;
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: composed only of `PhantomData`
 
-error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
-  --> $DIR/lint-ctypes.rs:61:23
+error: `extern` block uses type `fn()`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:62:23
    |
 LL |     pub fn fn_type(p: RustFn);
-   |                       ^^^^^^
+   |                       ^^^^^^ not FFI-safe
    |
    = help: consider using an `extern fn(...) -> ...` function pointer instead
+   = note: this function pointer has Rust-specific calling convention
 
-error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
-  --> $DIR/lint-ctypes.rs:62:24
+error: `extern` block uses type `fn()`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:63:24
    |
 LL |     pub fn fn_type2(p: fn());
-   |                        ^^^^
+   |                        ^^^^ not FFI-safe
    |
    = help: consider using an `extern fn(...) -> ...` function pointer instead
+   = note: this function pointer has Rust-specific calling convention
 
-error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
-  --> $DIR/lint-ctypes.rs:63:28
+error: `extern` block uses type `std::boxed::Box<u32>`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:64:28
    |
 LL |     pub fn fn_contained(p: RustBadRet);
-   |                            ^^^^^^^^^^
+   |                            ^^^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 
-error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
-  --> $DIR/lint-ctypes.rs:64:32
+error: `extern` block uses type `i128`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:65:32
    |
 LL |     pub fn transparent_i128(p: TransparentI128);
-   |                                ^^^^^^^^^^^^^^^
+   |                                ^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: 128-bit integers don't currently have a known stable ABI
 
-error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
-  --> $DIR/lint-ctypes.rs:65:31
+error: `extern` block uses type `str`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:66:31
    |
 LL |     pub fn transparent_str(p: TransparentStr);
-   |                               ^^^^^^^^^^^^^^
+   |                               ^^^^^^^^^^^^^^ not FFI-safe
    |
    = help: consider using `*const u8` and a length instead
+   = note: string slices have no C equivalent
 
-error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
-  --> $DIR/lint-ctypes.rs:66:30
+error: `extern` block uses type `std::boxed::Box<u32>`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:67:30
    |
 LL |     pub fn transparent_fn(p: TransparentBadFn);
-   |                              ^^^^^^^^^^^^^^^^
+   |                              ^^^^^^^^^^^^^^^^ not FFI-safe
    |
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
+   = note: this struct has unspecified layout
 
 error: aborting due to 20 previous errors
 
diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.rs b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs
new file mode 100644
index 00000000000..25d5f8ec68a
--- /dev/null
+++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.rs
@@ -0,0 +1,16 @@
+#![feature(type_alias_impl_trait)]
+
+#![deny(improper_ctypes)]
+
+type A = impl Fn();
+
+pub fn ret_closure() -> A {
+    || {}
+}
+
+extern "C" {
+    pub fn a(_: A);
+    //~^ ERROR `extern` block uses type `A`, which is not FFI-safe
+}
+
+fn main() {}
diff --git a/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr
new file mode 100644
index 00000000000..136d564d1ab
--- /dev/null
+++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr
@@ -0,0 +1,15 @@
+error: `extern` block uses type `A`, which is not FFI-safe
+  --> $DIR/opaque-ty-ffi-unsafe.rs:12:17
+   |
+LL |     pub fn a(_: A);
+   |                 ^ not FFI-safe
+   |
+note: lint level defined here
+  --> $DIR/opaque-ty-ffi-unsafe.rs:3:9
+   |
+LL | #![deny(improper_ctypes)]
+   |         ^^^^^^^^^^^^^^^
+   = note: opaque types have no C equivalent
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/union/union-repr-c.rs b/src/test/ui/union/union-repr-c.rs
index 658452d57f7..1367835e677 100644
--- a/src/test/ui/union/union-repr-c.rs
+++ b/src/test/ui/union/union-repr-c.rs
@@ -12,7 +12,7 @@ union W {
 
 extern "C" {
     static FOREIGN1: U; // OK
-    static FOREIGN2: W; //~ ERROR union has unspecified layout
+    static FOREIGN2: W; //~ ERROR `extern` block uses type `W`
 }
 
 fn main() {}
diff --git a/src/test/ui/union/union-repr-c.stderr b/src/test/ui/union/union-repr-c.stderr
index c60817a849a..c8bc0380dee 100644
--- a/src/test/ui/union/union-repr-c.stderr
+++ b/src/test/ui/union/union-repr-c.stderr
@@ -1,8 +1,8 @@
-error: `extern` block uses type `W` which is not FFI-safe: this union has unspecified layout
+error: `extern` block uses type `W`, which is not FFI-safe
   --> $DIR/union-repr-c.rs:15:22
    |
 LL |     static FOREIGN2: W;
-   |                      ^
+   |                      ^ not FFI-safe
    |
 note: lint level defined here
   --> $DIR/union-repr-c.rs:2:9
@@ -10,6 +10,7 @@ note: lint level defined here
 LL | #![deny(improper_ctypes)]
    |         ^^^^^^^^^^^^^^^
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union
+   = note: this union has unspecified layout
 note: type defined here
   --> $DIR/union-repr-c.rs:9:1
    |