Skip to content

Commit

Permalink
lint: prohibit fields with opaque types
Browse files Browse the repository at this point in the history
Opaque types cannot be used in extern declarations, and normally cannot
exist in fields - except with type aliases to `impl Trait` and
projections which normalize to them.

Signed-off-by: David Wood <david@davidtw.co>
  • Loading branch information
davidtwco committed Jun 19, 2020
1 parent d5b0737 commit 76ad38d
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 14 deletions.
39 changes: 25 additions & 14 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, ParamEnv, Ty, TyCtxt};
use rustc_middle::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt, TypeFoldable};
use rustc_span::source_map;
use rustc_span::symbol::sym;
use rustc_span::Span;
Expand Down Expand Up @@ -597,6 +597,22 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
}

/// Checks if the given field's type is "ffi-safe".
fn check_field_type_for_ffi(
&self,
cache: &mut FxHashSet<Ty<'tcx>>,
field: &ty::FieldDef,
substs: SubstsRef<'tcx>,
) -> FfiResult<'tcx> {
let field_ty = field.ty(self.cx.tcx, substs);
if field_ty.has_opaque_types() {
self.check_type_for_ffi(cache, field_ty)
} else {
let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty);
self.check_type_for_ffi(cache, field_ty)
}
}

/// Checks if the given type is "ffi-safe" (has a stable, well-defined
/// representation which can be exported to C code).
fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
Expand Down Expand Up @@ -654,11 +670,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if let Some(field) =
def.transparent_newtype_field(cx, self.cx.param_env)
{
let field_ty = cx.normalize_erasing_regions(
self.cx.param_env,
field.ty(cx, substs),
);
self.check_type_for_ffi(cache, field_ty)
self.check_field_type_for_ffi(cache, field, substs)
} else {
FfiSafe
}
Expand All @@ -667,11 +679,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
// actually safe.
let mut all_phantom = true;
for field in &def.non_enum_variant().fields {
let field_ty = cx.normalize_erasing_regions(
self.cx.param_env,
field.ty(cx, substs),
);
let r = self.check_type_for_ffi(cache, field_ty);
let r = self.check_field_type_for_ffi(cache, field, substs);
match r {
FfiSafe => {
all_phantom = false;
Expand Down Expand Up @@ -886,6 +894,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {

ty::Foreign(..) => FfiSafe,

// While opaque types are checked for earlier, if a projection in a struct field
// normalizes to an opaque type, then it will reach this branch.
ty::Opaque(..) => {
FfiUnsafe { ty, reason: "opaque types have no C equivalent", help: None }
}

ty::Param(..)
| ty::Infer(..)
| ty::Bound(..)
Expand All @@ -895,7 +909,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
| ty::GeneratorWitness(..)
| ty::Placeholder(..)
| ty::Projection(..)
| ty::Opaque(..)
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
}
}
Expand Down Expand Up @@ -925,8 +938,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}

fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
use rustc_middle::ty::TypeFoldable;

struct ProhibitOpaqueTypes<'tcx> {
ty: Option<Ty<'tcx>>,
};
Expand Down
29 changes: 29 additions & 0 deletions src/test/ui/lint/lint-ctypes-73249-2.rs
@@ -0,0 +1,29 @@
#![feature(type_alias_impl_trait)]
#![deny(improper_ctypes)]

pub trait Baz { }

impl Baz for () { }

type Qux = impl Baz;

fn assign() -> Qux {}

pub trait Foo {
type Assoc: 'static;
}

impl Foo for () {
type Assoc = Qux;
}

#[repr(transparent)]
pub struct A<T: Foo> {
x: &'static <T as Foo>::Assoc,
}

extern "C" {
pub fn lint_me() -> A<()>; //~ ERROR: uses type `impl Baz`
}

fn main() {}
15 changes: 15 additions & 0 deletions src/test/ui/lint/lint-ctypes-73249-2.stderr
@@ -0,0 +1,15 @@
error: `extern` block uses type `impl Baz`, which is not FFI-safe
--> $DIR/lint-ctypes-73249-2.rs:26:25
|
LL | pub fn lint_me() -> A<()>;
| ^^^^^ not FFI-safe
|
note: the lint level is defined here
--> $DIR/lint-ctypes-73249-2.rs:2:9
|
LL | #![deny(improper_ctypes)]
| ^^^^^^^^^^^^^^^
= note: opaque types have no C equivalent

error: aborting due to previous error

21 changes: 21 additions & 0 deletions src/test/ui/lint/lint-ctypes-73249-3.rs
@@ -0,0 +1,21 @@
#![feature(type_alias_impl_trait)]
#![deny(improper_ctypes)]

pub trait Baz { }

impl Baz for u32 { }

type Qux = impl Baz;

fn assign() -> Qux { 3 }

#[repr(C)]
pub struct A {
x: Qux,
}

extern "C" {
pub fn lint_me() -> A; //~ ERROR: uses type `impl Baz`
}

fn main() {}
15 changes: 15 additions & 0 deletions src/test/ui/lint/lint-ctypes-73249-3.stderr
@@ -0,0 +1,15 @@
error: `extern` block uses type `impl Baz`, which is not FFI-safe
--> $DIR/lint-ctypes-73249-3.rs:18:25
|
LL | pub fn lint_me() -> A;
| ^ not FFI-safe
|
note: the lint level is defined here
--> $DIR/lint-ctypes-73249-3.rs:2:9
|
LL | #![deny(improper_ctypes)]
| ^^^^^^^^^^^^^^^
= note: opaque types have no C equivalent

error: aborting due to previous error

21 changes: 21 additions & 0 deletions src/test/ui/lint/lint-ctypes-73249-5.rs
@@ -0,0 +1,21 @@
#![feature(type_alias_impl_trait)]
#![deny(improper_ctypes)]

pub trait Baz { }

impl Baz for u32 { }

type Qux = impl Baz;

fn assign() -> Qux { 3 }

#[repr(transparent)]
pub struct A {
x: Qux,
}

extern "C" {
pub fn lint_me() -> A; //~ ERROR: uses type `impl Baz`
}

fn main() {}
15 changes: 15 additions & 0 deletions src/test/ui/lint/lint-ctypes-73249-5.stderr
@@ -0,0 +1,15 @@
error: `extern` block uses type `impl Baz`, which is not FFI-safe
--> $DIR/lint-ctypes-73249-5.rs:18:25
|
LL | pub fn lint_me() -> A;
| ^ not FFI-safe
|
note: the lint level is defined here
--> $DIR/lint-ctypes-73249-5.rs:2:9
|
LL | #![deny(improper_ctypes)]
| ^^^^^^^^^^^^^^^
= note: opaque types have no C equivalent

error: aborting due to previous error

0 comments on commit 76ad38d

Please sign in to comment.