Skip to content

Commit 76ad38d

Browse files
committed
lint: prohibit fields with opaque types
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 <[email protected]>
1 parent d5b0737 commit 76ad38d

7 files changed

+141
-14
lines changed

src/librustc_lint/types.rs

+25-14
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_index::vec::Idx;
1111
use rustc_middle::mir::interpret::{sign_extend, truncate};
1212
use rustc_middle::ty::layout::{IntegerExt, SizeSkeleton};
1313
use rustc_middle::ty::subst::SubstsRef;
14-
use rustc_middle::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
14+
use rustc_middle::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt, TypeFoldable};
1515
use rustc_span::source_map;
1616
use rustc_span::symbol::sym;
1717
use rustc_span::Span;
@@ -597,6 +597,22 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
597597
}
598598
}
599599

600+
/// Checks if the given field's type is "ffi-safe".
601+
fn check_field_type_for_ffi(
602+
&self,
603+
cache: &mut FxHashSet<Ty<'tcx>>,
604+
field: &ty::FieldDef,
605+
substs: SubstsRef<'tcx>,
606+
) -> FfiResult<'tcx> {
607+
let field_ty = field.ty(self.cx.tcx, substs);
608+
if field_ty.has_opaque_types() {
609+
self.check_type_for_ffi(cache, field_ty)
610+
} else {
611+
let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty);
612+
self.check_type_for_ffi(cache, field_ty)
613+
}
614+
}
615+
600616
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
601617
/// representation which can be exported to C code).
602618
fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
@@ -654,11 +670,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
654670
if let Some(field) =
655671
def.transparent_newtype_field(cx, self.cx.param_env)
656672
{
657-
let field_ty = cx.normalize_erasing_regions(
658-
self.cx.param_env,
659-
field.ty(cx, substs),
660-
);
661-
self.check_type_for_ffi(cache, field_ty)
673+
self.check_field_type_for_ffi(cache, field, substs)
662674
} else {
663675
FfiSafe
664676
}
@@ -667,11 +679,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
667679
// actually safe.
668680
let mut all_phantom = true;
669681
for field in &def.non_enum_variant().fields {
670-
let field_ty = cx.normalize_erasing_regions(
671-
self.cx.param_env,
672-
field.ty(cx, substs),
673-
);
674-
let r = self.check_type_for_ffi(cache, field_ty);
682+
let r = self.check_field_type_for_ffi(cache, field, substs);
675683
match r {
676684
FfiSafe => {
677685
all_phantom = false;
@@ -886,6 +894,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
886894

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

897+
// While opaque types are checked for earlier, if a projection in a struct field
898+
// normalizes to an opaque type, then it will reach this branch.
899+
ty::Opaque(..) => {
900+
FfiUnsafe { ty, reason: "opaque types have no C equivalent", help: None }
901+
}
902+
889903
ty::Param(..)
890904
| ty::Infer(..)
891905
| ty::Bound(..)
@@ -895,7 +909,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
895909
| ty::GeneratorWitness(..)
896910
| ty::Placeholder(..)
897911
| ty::Projection(..)
898-
| ty::Opaque(..)
899912
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
900913
}
901914
}
@@ -925,8 +938,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
925938
}
926939

927940
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
928-
use rustc_middle::ty::TypeFoldable;
929-
930941
struct ProhibitOpaqueTypes<'tcx> {
931942
ty: Option<Ty<'tcx>>,
932943
};
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![feature(type_alias_impl_trait)]
2+
#![deny(improper_ctypes)]
3+
4+
pub trait Baz { }
5+
6+
impl Baz for () { }
7+
8+
type Qux = impl Baz;
9+
10+
fn assign() -> Qux {}
11+
12+
pub trait Foo {
13+
type Assoc: 'static;
14+
}
15+
16+
impl Foo for () {
17+
type Assoc = Qux;
18+
}
19+
20+
#[repr(transparent)]
21+
pub struct A<T: Foo> {
22+
x: &'static <T as Foo>::Assoc,
23+
}
24+
25+
extern "C" {
26+
pub fn lint_me() -> A<()>; //~ ERROR: uses type `impl Baz`
27+
}
28+
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: `extern` block uses type `impl Baz`, which is not FFI-safe
2+
--> $DIR/lint-ctypes-73249-2.rs:26:25
3+
|
4+
LL | pub fn lint_me() -> A<()>;
5+
| ^^^^^ not FFI-safe
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-ctypes-73249-2.rs:2:9
9+
|
10+
LL | #![deny(improper_ctypes)]
11+
| ^^^^^^^^^^^^^^^
12+
= note: opaque types have no C equivalent
13+
14+
error: aborting due to previous error
15+
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![feature(type_alias_impl_trait)]
2+
#![deny(improper_ctypes)]
3+
4+
pub trait Baz { }
5+
6+
impl Baz for u32 { }
7+
8+
type Qux = impl Baz;
9+
10+
fn assign() -> Qux { 3 }
11+
12+
#[repr(C)]
13+
pub struct A {
14+
x: Qux,
15+
}
16+
17+
extern "C" {
18+
pub fn lint_me() -> A; //~ ERROR: uses type `impl Baz`
19+
}
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: `extern` block uses type `impl Baz`, which is not FFI-safe
2+
--> $DIR/lint-ctypes-73249-3.rs:18:25
3+
|
4+
LL | pub fn lint_me() -> A;
5+
| ^ not FFI-safe
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-ctypes-73249-3.rs:2:9
9+
|
10+
LL | #![deny(improper_ctypes)]
11+
| ^^^^^^^^^^^^^^^
12+
= note: opaque types have no C equivalent
13+
14+
error: aborting due to previous error
15+
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![feature(type_alias_impl_trait)]
2+
#![deny(improper_ctypes)]
3+
4+
pub trait Baz { }
5+
6+
impl Baz for u32 { }
7+
8+
type Qux = impl Baz;
9+
10+
fn assign() -> Qux { 3 }
11+
12+
#[repr(transparent)]
13+
pub struct A {
14+
x: Qux,
15+
}
16+
17+
extern "C" {
18+
pub fn lint_me() -> A; //~ ERROR: uses type `impl Baz`
19+
}
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: `extern` block uses type `impl Baz`, which is not FFI-safe
2+
--> $DIR/lint-ctypes-73249-5.rs:18:25
3+
|
4+
LL | pub fn lint_me() -> A;
5+
| ^ not FFI-safe
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/lint-ctypes-73249-5.rs:2:9
9+
|
10+
LL | #![deny(improper_ctypes)]
11+
| ^^^^^^^^^^^^^^^
12+
= note: opaque types have no C equivalent
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)