about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_lint/types.rs33
-rw-r--r--src/test/ui/lint/opaque-ty-ffi-unsafe.rs16
-rw-r--r--src/test/ui/lint/opaque-ty-ffi-unsafe.stderr14
3 files changed, 63 insertions, 0 deletions
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index bdb6844920d..63fe11f3443 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -859,7 +859,40 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         }
     }
 
+    fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
+        use crate::rustc::ty::TypeFoldable;
+
+        struct ProhibitOpaqueTypes<'a, 'tcx> {
+            cx: &'a LateContext<'a, 'tcx>,
+            sp: Span,
+        };
+
+        impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> {
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
+                if let ty::Opaque(..) = ty.sty {
+                    self.cx.span_lint(IMPROPER_CTYPES,
+                        self.sp,
+                        &format!("`extern` block uses type `{}` which is not FFI-safe: \
+                                  opaque types have no C equivalent", ty));
+                    true
+                } else {
+                    ty.super_visit_with(self)
+                }
+            }
+        }
+
+        let mut visitor = ProhibitOpaqueTypes { cx: self.cx, sp };
+        ty.visit_with(&mut visitor)
+    }
+
     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);
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..907ad068035
--- /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: opaque types have no C equivalent
+}
+
+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..6e234aa300b
--- /dev/null
+++ b/src/test/ui/lint/opaque-ty-ffi-unsafe.stderr
@@ -0,0 +1,14 @@
+error: `extern` block uses type `A` which is not FFI-safe: opaque types have no C equivalent
+  --> $DIR/opaque-ty-ffi-unsafe.rs:12:17
+   |
+LL |     pub fn a(_: A);
+   |                 ^
+   |
+note: lint level defined here
+  --> $DIR/opaque-ty-ffi-unsafe.rs:3:9
+   |
+LL | #![deny(improper_ctypes)]
+   |         ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+