Skip to content

Commit e866d07

Browse files
committed
lint/ctypes: Don't warn on non-unsized structs with PhantomData.
Fixes #34798
1 parent aed6410 commit e866d07

File tree

3 files changed

+79
-7
lines changed

3 files changed

+79
-7
lines changed

src/librustc_lint/types.rs

+39-7
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
341341

342342
enum FfiResult {
343343
FfiSafe,
344+
FfiPhantom,
344345
FfiUnsafe(&'static str),
345346
FfiBadStruct(DefId, &'static str),
346347
FfiBadUnion(DefId, &'static str),
@@ -385,8 +386,11 @@ fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
385386
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
386387
/// Check if the given type is "ffi-safe" (has a stable, well-defined
387388
/// representation which can be exported to C code).
388-
fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult {
389+
fn check_type_for_ffi(&self,
390+
cache: &mut FxHashSet<Ty<'tcx>>,
391+
ty: Ty<'tcx>) -> FfiResult {
389392
use self::FfiResult::*;
393+
390394
let cx = self.cx.tcx;
391395

392396
// Protect against infinite recursion, for example
@@ -399,6 +403,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
399403

400404
match ty.sty {
401405
ty::TyAdt(def, substs) => {
406+
if def.is_phantom_data() {
407+
return FfiPhantom;
408+
}
402409
match def.adt_kind() {
403410
AdtKind::Struct => {
404411
if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
@@ -407,18 +414,22 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
407414
consider adding a #[repr(C)] attribute to the type");
408415
}
409416

410-
// We can't completely trust repr(C) markings; make sure the
411-
// fields are actually safe.
412417
if def.struct_variant().fields.is_empty() {
413418
return FfiUnsafe("found zero-size struct in foreign module, consider \
414419
adding a member to this struct");
415420
}
416421

422+
// We can't completely trust repr(C) markings; make sure the
423+
// fields are actually safe.
424+
let mut all_phantom = true;
417425
for field in &def.struct_variant().fields {
418426
let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
419427
let r = self.check_type_for_ffi(cache, field_ty);
420428
match r {
421-
FfiSafe => {}
429+
FfiSafe => {
430+
all_phantom = false;
431+
}
432+
FfiPhantom => {}
422433
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
423434
return r;
424435
}
@@ -427,7 +438,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
427438
}
428439
}
429440
}
430-
FfiSafe
441+
442+
if all_phantom { FfiPhantom } else { FfiSafe }
431443
}
432444
AdtKind::Union => {
433445
if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
@@ -436,11 +448,20 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
436448
consider adding a #[repr(C)] attribute to the type");
437449
}
438450

451+
if def.struct_variant().fields.is_empty() {
452+
return FfiUnsafe("found zero-size union in foreign module, consider \
453+
adding a member to this union");
454+
}
455+
456+
let mut all_phantom = true;
439457
for field in &def.struct_variant().fields {
440458
let field_ty = cx.normalize_associated_type(&field.ty(cx, substs));
441459
let r = self.check_type_for_ffi(cache, field_ty);
442460
match r {
443-
FfiSafe => {}
461+
FfiSafe => {
462+
all_phantom = false;
463+
}
464+
FfiPhantom => {}
444465
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
445466
return r;
446467
}
@@ -449,7 +470,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
449470
}
450471
}
451472
}
452-
FfiSafe
473+
474+
if all_phantom { FfiPhantom } else { FfiSafe }
453475
}
454476
AdtKind::Enum => {
455477
if def.variants.is_empty() {
@@ -500,6 +522,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
500522
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
501523
return r;
502524
}
525+
FfiPhantom => {
526+
return FfiBadEnum(def.did,
527+
"Found phantom data in enum variant");
528+
}
503529
FfiUnsafe(s) => {
504530
return FfiBadEnum(def.did, s);
505531
}
@@ -593,6 +619,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
593619

594620
match self.check_type_for_ffi(&mut FxHashSet(), ty) {
595621
FfiResult::FfiSafe => {}
622+
FfiResult::FfiPhantom => {
623+
self.cx.span_lint(IMPROPER_CTYPES,
624+
sp,
625+
&format!("found zero-sized type composed only \
626+
of phantom-data in a foreign-function."));
627+
}
596628
FfiResult::FfiUnsafe(s) => {
597629
self.cx.span_lint(IMPROPER_CTYPES, sp, s);
598630
}

src/test/compile-fail/lint-ctypes.rs

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub type RustBadRet = extern fn() -> Box<u32>;
2929
pub type CVoidRet = ();
3030
pub struct Foo;
3131

32+
#[repr(C)]
33+
pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
34+
3235
extern {
3336
pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without
3437
pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without
@@ -40,6 +43,9 @@ extern {
4043
pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type
4144
pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type
4245
pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct
46+
pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR found zero-sized type
47+
pub fn zero_size_phantom_toplevel()
48+
-> ::std::marker::PhantomData<bool>; //~ ERROR: found zero-sized type
4349
pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust
4450
pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust
4551
pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without

src/test/run-pass/issue-34798.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![forbid(improper_ctypes)]
12+
#![allow(dead_code)]
13+
14+
#[repr(C)]
15+
pub struct Foo {
16+
size: u8,
17+
__value: ::std::marker::PhantomData<i32>,
18+
}
19+
20+
#[repr(C)]
21+
pub struct ZeroSizeWithPhantomData<T>(::std::marker::PhantomData<T>);
22+
23+
#[repr(C)]
24+
pub struct Bar {
25+
size: u8,
26+
baz: ZeroSizeWithPhantomData<i32>,
27+
}
28+
29+
extern "C" {
30+
pub fn bar(_: *mut Foo, _: *mut Bar);
31+
}
32+
33+
fn main() {
34+
}

0 commit comments

Comments
 (0)