]> git.lizzy.rs Git - rust.git/commitdiff
Fix clashing_extern_declarations false positive.
authorjumbatm <30644300+jumbatm@users.noreply.github.com>
Mon, 24 Aug 2020 16:24:05 +0000 (02:24 +1000)
committerjumbatm <30644300+jumbatm@users.noreply.github.com>
Mon, 24 Aug 2020 16:30:16 +0000 (02:30 +1000)
Fixes a false positive for transparent newtype with a non-zero member.

src/librustc_lint/builtin.rs
src/librustc_lint/types.rs
src/test/ui/lint/clashing-extern-fn.rs
src/test/ui/lint/clashing-extern-fn.stderr

index b337bf0a3f9226f7fecb3ccdfc4375a4791fd1b3..bfdf3e3b3956a2f2351153369e19384b47a16ffd 100644 (file)
@@ -2162,6 +2162,38 @@ fn structurally_same_type_impl<'tcx>(
             ckind: CItemKind,
         ) -> bool {
             debug!("structurally_same_type_impl(cx, a = {:?}, b = {:?})", a, b);
+            let tcx = cx.tcx;
+
+            // Given a transparent newtype, reach through and grab the inner
+            // type unless the newtype makes the type non-null.
+            let non_transparent_ty = |ty: Ty<'tcx>| -> Ty<'tcx> {
+                let mut ty = ty;
+                loop {
+                    if let ty::Adt(def, substs) = ty.kind {
+                        let is_transparent = def.subst(tcx, substs).repr.transparent();
+                        let is_enum = def.is_enum();
+                        let is_non_null = crate::types::guaranteed_nonnull_optimization(tcx, &def);
+                        debug!(
+                            "non_transparent_ty({:?}) -- type is transparent? {}, type is enum? {}, type is non-null? {}",
+                            ty, is_transparent, is_enum, is_non_null
+                        );
+                        if is_transparent && !is_enum && !is_non_null {
+                            ty = def
+                                .non_enum_variant()
+                                .transparent_newtype_field(tcx)
+                                .unwrap()
+                                .ty(tcx, substs);
+                            continue;
+                        }
+                    }
+                    debug!("non_transparent_ty -> {:?}", ty);
+                    return ty;
+                }
+            };
+
+            let a = non_transparent_ty(a);
+            let b = non_transparent_ty(b);
+
             if !seen_types.insert((a, b)) {
                 // We've encountered a cycle. There's no point going any further -- the types are
                 // structurally the same.
index 4ca5f23ebfe6c22bfd2ca99b26862e26a91fab9e..849e8df1e5fdbf7a97dabc2737f4463a5940f610 100644 (file)
@@ -11,7 +11,7 @@
 use rustc_middle::mir::interpret::{sign_extend, truncate};
 use rustc_middle::ty::layout::{IntegerExt, SizeSkeleton};
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, AdtKind, Ty, TypeFoldable};
+use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
 use rustc_span::source_map;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
@@ -527,22 +527,26 @@ enum FfiResult<'tcx> {
     FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> },
 }
 
+crate fn guaranteed_nonnull_optimization<'tcx>(tcx: TyCtxt<'tcx>, def: &ty::AdtDef) -> bool {
+    tcx.get_attrs(def.did)
+        .iter()
+        .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed))
+}
+
 /// Is type known to be non-null?
-fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
+crate fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKind) -> bool {
     let tcx = cx.tcx;
     match ty.kind {
         ty::FnPtr(_) => true,
         ty::Ref(..) => true,
         ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
         ty::Adt(def, substs) if def.repr.transparent() && !def.is_union() => {
-            let guaranteed_nonnull_optimization = tcx
-                .get_attrs(def.did)
-                .iter()
-                .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed));
+            let marked_non_null = guaranteed_nonnull_optimization(tcx, &def);
 
-            if guaranteed_nonnull_optimization {
+            if marked_non_null {
                 return true;
             }
+
             for variant in &def.variants {
                 if let Some(field) = variant.transparent_newtype_field(tcx) {
                     if ty_is_known_nonnull(cx, field.ty(tcx, substs), mode) {
index d6ac7ccccc77b1c6b111ac80855efa81d0d5eb16..a2de01254477df330e3ada3948559d7170e270c2 100644 (file)
@@ -258,6 +258,62 @@ mod b {
     }
 }
 
+// See #75739
+mod non_zero_transparent {
+    mod a1 {
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f1() -> NonZeroUsize;
+        }
+    }
+
+    mod b1 {
+        #[repr(transparent)]
+        struct X(NonZeroUsize);
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f1() -> X;
+        }
+    }
+
+    mod a2 {
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f2() -> NonZeroUsize;
+        }
+    }
+
+    mod b2 {
+        #[repr(transparent)]
+        struct X1(NonZeroUsize);
+
+        #[repr(transparent)]
+        struct X(X1);
+
+        use std::num::NonZeroUsize;
+        extern "C" {
+            // Same case as above, but with two layers of newtyping.
+            fn f2() -> X;
+        }
+    }
+
+    mod a3 {
+        #[repr(transparent)]
+        struct X(core::ptr::NonNull<i32>);
+
+        use std::num::NonZeroUsize;
+        extern "C" {
+            fn f3() -> X;
+        }
+    }
+
+    mod b3 {
+        extern "C" {
+            fn f3() -> core::ptr::NonNull<i32>;
+        }
+    }
+}
+
 mod null_optimised_enums {
     mod a {
         extern "C" {
index cca0c4c59eb197166f08f901e65b006c604281da..f2e53e4a3cc0e512171661ee8cf8d32a3b2ca2b5 100644 (file)
@@ -166,7 +166,7 @@ LL |             fn non_null_ptr() -> *const usize;
               found `unsafe extern "C" fn() -> *const usize`
 
 warning: `option_non_zero_usize_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:281:13
+  --> $DIR/clashing-extern-fn.rs:337:13
    |
 LL |             fn option_non_zero_usize_incorrect() -> usize;
    |             ---------------------------------------------- `option_non_zero_usize_incorrect` previously declared here
@@ -178,7 +178,7 @@ LL |             fn option_non_zero_usize_incorrect() -> isize;
               found `unsafe extern "C" fn() -> isize`
 
 warning: `option_non_null_ptr_incorrect` redeclared with a different signature
-  --> $DIR/clashing-extern-fn.rs:283:13
+  --> $DIR/clashing-extern-fn.rs:339:13
    |
 LL |             fn option_non_null_ptr_incorrect() -> *const usize;
    |             --------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here