Skip to content

Commit

Permalink
Fix clashing_extern_declarations false positive.
Browse files Browse the repository at this point in the history
Fixes a false positive for transparent newtype with a non-zero member.
  • Loading branch information
jumbatm committed Aug 24, 2020
1 parent 0301700 commit f38eb93
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 9 deletions.
32 changes: 32 additions & 0 deletions src/librustc_lint/builtin.rs
Expand Up @@ -2162,6 +2162,38 @@ impl ClashingExternDeclarations {
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.
Expand Down
18 changes: 11 additions & 7 deletions src/librustc_lint/types.rs
Expand Up @@ -11,7 +11,7 @@ use rustc_index::vec::Idx;
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};
Expand Down Expand Up @@ -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) {
Expand Down
56 changes: 56 additions & 0 deletions src/test/ui/lint/clashing-extern-fn.rs
Expand Up @@ -258,6 +258,62 @@ mod non_zero_and_non_null {
}
}

// 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" {
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/lint/clashing-extern-fn.stderr
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit f38eb93

Please sign in to comment.