/// Check if the given type is "ffi-safe" (has a stable, well-defined
/// representation which can be exported to C code).
Check if the given type is "ffi-safe" (has a stable, well-defined
representation which can be exported to C code).
Check 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 FnvHashSet<Ty<'tcx>>,
                          ty: Ty<'tcx>)
                          -> FfiResult {
        use self::FfiResult::*;
        let cx = &self.cx.tcx;
        // Protect against infinite recursion, for example
        // `struct S(*mut S);`.
        // FIXME: A recursion limit is necessary as well, for irregular
        // recusive types.
        if !cache.insert(ty) {
            return FfiSafe;
        }
        match ty.sty {
            ty::TyStruct(def, substs) => {
                if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
                    return FfiUnsafe(
                        "found struct without foreign-function-safe \
                         representation annotation in foreign module, \
                         consider adding a #[repr(C)] attribute to \
                         the type");
                }
                // We can't completely trust repr(C) markings; make sure the
                // fields are actually safe.
                if def.struct_variant().fields.is_empty() {
                    return FfiUnsafe(
                        "found zero-size struct in foreign module, consider \
                         adding a member to this struct");
                }
                for field in &def.struct_variant().fields {
                    let field_ty = infer::normalize_associated_type(cx, &field.ty(cx, substs));
                    let r = self.check_type_for_ffi(cache, field_ty);
                    match r {
                        FfiSafe => {}
                        FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
                        FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
                    }
                }
                FfiSafe
            }
            ty::TyEnum(def, substs) => {
                if def.variants.is_empty() {
                    // Empty enums are okay... although sort of useless.
                    return FfiSafe
                }
                // Check for a repr() attribute to specify the size of the
                // discriminant.
                let repr_hints = cx.lookup_repr_hints(def.did);
                match &**repr_hints {
                    [] => {
                        // Special-case types like `Option<extern fn()>`.
                        if !is_repr_nullable_ptr(cx, def, substs) {
                            return FfiUnsafe(
                                "found enum without foreign-function-safe \
                                 representation annotation in foreign module, \
                                 consider adding a #[repr(...)] attribute to \
                                 the type")
                        }
                    }
                    [ref hint] => {
                        if !hint.is_ffi_safe() {
                            // FIXME: This shouldn't be reachable: we should check
                            // this earlier.
                            return FfiUnsafe(
                                "enum has unexpected #[repr(...)] attribute")
                        }
                        // Enum with an explicitly sized discriminant; either
                        // a C-style enum or a discriminated union.
                        // The layout of enum variants is implicitly repr(C).
                        // FIXME: Is that correct?
                    }
                    _ => {
                        // FIXME: This shouldn't be reachable: we should check
                        // this earlier.
                        return FfiUnsafe(
                            "enum has too many #[repr(...)] attributes");
                    }
                }
                // Check the contained variants.
                for variant in &def.variants {
                    for field in &variant.fields {
                        let arg = infer::normalize_associated_type(cx, &field.ty(cx, substs));
                        let r = self.check_type_for_ffi(cache, arg);
                        match r {
                            FfiSafe => {}
                            FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
                            FfiUnsafe(s) => { return FfiBadEnum(def.did, s); }
                        }
                    }
                }
                FfiSafe
            }
            ty::TyInt(hir::TyIs) => {
                FfiUnsafe("found Rust type `isize` in foreign module, while \
                          `libc::c_int` or `libc::c_long` should be used")
            }
            ty::TyUint(hir::TyUs) => {
                FfiUnsafe("found Rust type `usize` in foreign module, while \
                          `libc::c_uint` or `libc::c_ulong` should be used")
            }
            ty::TyChar => {
                FfiUnsafe("found Rust type `char` in foreign module, while \
                           `u32` or `libc::wchar_t` should be used")
            }
            // Primitive types with a stable representation.
            ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
            ty::TyFloat(..) => FfiSafe,
            ty::TyBox(..) => {
                FfiUnsafe("found Rust type Box<_> in foreign module, \
                           consider using a raw pointer instead")
            }
            ty::TySlice(_) => {
                FfiUnsafe("found Rust slice type in foreign module, \
                           consider using a raw pointer instead")
            }
            ty::TyTrait(..) => {
                FfiUnsafe("found Rust trait type in foreign module, \
                           consider using a raw pointer instead")
            }
            ty::TyStr => {
                FfiUnsafe("found Rust type `str` in foreign module; \
                           consider using a `*const libc::c_char`")
            }
            ty::TyTuple(_) => {
                FfiUnsafe("found Rust tuple type in foreign module; \
                           consider using a struct instead`")
            }
            ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
                self.check_type_for_ffi(cache, m.ty)
            }
            ty::TyArray(ty, _) => {
                self.check_type_for_ffi(cache, ty)
            }
            ty::TyBareFn(None, bare_fn) => {
                match bare_fn.abi {
                    abi::Rust |
                    abi::RustIntrinsic |
                    abi::PlatformIntrinsic |
                    abi::RustCall => {
                        return FfiUnsafe(
                            "found function pointer with Rust calling \
                             convention in foreign module; consider using an \
                             `extern` function pointer")
                    }
                    _ => {}
                }
                let sig = cx.erase_late_bound_regions(&bare_fn.sig);
                match sig.output {
                    ty::FnDiverging => {}
                    ty::FnConverging(output) => {
                        if !output.is_nil() {
                            let r = self.check_type_for_ffi(cache, output);
                            match r {
                                FfiSafe => {}
                                _ => { return r; }
                            }
                        }
                    }
                }
                for arg in sig.inputs {
                    let r = self.check_type_for_ffi(cache, arg);
                    match r {
                        FfiSafe => {}
                        _ => { return r; }
                    }
                }
                FfiSafe
            }
            ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
            ty::TyClosure(..) | ty::TyProjection(..) |
            ty::TyBareFn(Some(_), _) => {
                panic!("Unexpected type in foreign function")
            }
        }
    }
fn check_type_for_ffi ( & self , cache : & mut FnvHashSet < Ty < ' tcx > > , ty : Ty < ' tcx > ) -> FfiResult { use self :: FfiResult :: * ; let cx = & self . cx . tcx ; // Protect against infinite recursion, for example // `struct S(*mut S);`. // FIXME: A recursion limit is necessary as well, for irregular // recusive types. if ! cache . insert ( ty ) { return FfiSafe ; } match ty . sty { ty :: TyStruct ( def , substs ) => { if ! cx . lookup_repr_hints ( def . did ) . contains ( & attr :: ReprExtern ) { return FfiUnsafe ( " \
 \
 \
 " ) ; } // We can't completely trust repr(C) markings; make sure the // fields are actually safe. if def . struct_variant ( ) . fields . is_empty ( ) { return FfiUnsafe ( " \
 " ) ; } for field in & def . struct_variant ( ) . fields { let field_ty = infer :: normalize_associated_type ( cx , & field . ty ( cx , substs ) ) ; let r = self . check_type_for_ffi ( cache , field_ty ) ; match r { FfiSafe => { } FfiBadStruct ( .. ) | FfiBadEnum ( .. ) => { return r ; } FfiUnsafe ( s ) => { return FfiBadStruct ( def . did , s ) ; } } } FfiSafe } ty :: TyEnum ( def , substs ) => { if def . variants . is_empty ( ) { // Empty enums are okay... although sort of useless. return FfiSafe } // Check for a repr() attribute to specify the size of the // discriminant. let repr_hints = cx . lookup_repr_hints ( def . did ) ; match & * * repr_hints { [ ] => { // Special-case types like `Option<extern fn()>`. if ! is_repr_nullable_ptr ( cx , def , substs ) { return FfiUnsafe ( " \
 \
 \
 " ) } } [ ref hint ] => { if ! hint . is_ffi_safe ( ) { // FIXME: This shouldn't be reachable: we should check // this earlier. return FfiUnsafe ( " " ) } // Enum with an explicitly sized discriminant; either // a C-style enum or a discriminated union. // The layout of enum variants is implicitly repr(C). // FIXME: Is that correct? } _ => { // FIXME: This shouldn't be reachable: we should check // this earlier. return FfiUnsafe ( " " ) ; } } // Check the contained variants. for variant in & def . variants { for field in & variant . fields { let arg = infer :: normalize_associated_type ( cx , & field . ty ( cx , substs ) ) ; let r = self . check_type_for_ffi ( cache , arg ) ; match r { FfiSafe => { } FfiBadStruct ( .. ) | FfiBadEnum ( .. ) => { return r ; } FfiUnsafe ( s ) => { return FfiBadEnum ( def . did , s ) ; } } } } FfiSafe } ty :: TyInt ( hir :: TyIs ) => { FfiUnsafe ( " \
 " ) } ty :: TyUint ( hir :: TyUs ) => { FfiUnsafe ( " \
 " ) } ty :: TyChar => { FfiUnsafe ( " \
 " ) } // Primitive types with a stable representation. ty :: TyBool | ty :: TyInt ( .. ) | ty :: TyUint ( .. ) | ty :: TyFloat ( .. ) => FfiSafe , ty :: TyBox ( .. ) => { FfiUnsafe ( " \
 " ) } ty :: TySlice ( _ ) => { FfiUnsafe ( " \
 " ) } ty :: TyTrait ( .. ) => { FfiUnsafe ( " \
 " ) } ty :: TyStr => { FfiUnsafe ( " \
 " ) } ty :: TyTuple ( _ ) => { FfiUnsafe ( " \
 " ) } ty :: TyRawPtr ( ref m ) | ty :: TyRef ( _ , ref m ) => { self . check_type_for_ffi ( cache , m . ty ) } ty :: TyArray ( ty , _ ) => { self . check_type_for_ffi ( cache , ty ) } ty :: TyBareFn ( None , bare_fn ) => { match bare_fn . abi { abi :: Rust | abi :: RustIntrinsic | abi :: PlatformIntrinsic | abi :: RustCall => { return FfiUnsafe ( " \
 \
 " ) } _ => { } } let sig = cx . erase_late_bound_regions ( & bare_fn . sig ) ; match sig . output { ty :: FnDiverging => { } ty :: FnConverging ( output ) => { if ! output . is_nil ( ) { let r = self . check_type_for_ffi ( cache , output ) ; match r { FfiSafe => { } _ => { return r ; } } } } } for arg in sig . inputs { let r = self . check_type_for_ffi ( cache , arg ) ; match r { FfiSafe => { } _ => { return r ; } } } FfiSafe } ty :: TyParam ( .. ) | ty :: TyInfer ( .. ) | ty :: TyError | ty :: TyClosure ( .. ) | ty :: TyProjection ( .. ) | ty :: TyBareFn ( Some ( _ ) , _ ) => { panic ! ( " " ) } } }
Check if the given type is "ffi-safe" (has a stable, well-defined
representation which can be exported to C code).
Check if the given type is " ffi - safe " ( has a stable well - defined representation which can be exported to C code ) .
&self
cache
&mut FnvHashSet<Ty<'tcx>>
ty
Ty<'tcx>