Change:
Arrays are passed to C as a raw pointers, which means that does not move it, so this example is confusing at best. () Errors: If one does not try to use the moved value, this will silently compile, but will be deallocated as soon as the function returns, yet the C code could still try to read (or even write - the code above doesn't make it clear what C can actually do with the pointer...) to it. It would be better if we would require code to be more explicit about this, e.g., by writing: instead. This makes it clear that doesn't own the array, how many elements are expected behind the pointer, and whether the foreign function only reads or also might write to it. We could avoid breaking changes due to updating C FFI code by allowing people to still call but treating it a as a unique or shared borrow depending on the mutability of the FFI declaration, and then applying a coercion to the raw pointer, while simultaneously emitting a warning to users that they should be more explicit and write instead.
FWIW, my preferred solution to this problem would be to just warn when arrays are not used behind a pointer on foreign function declarations, and maybe ban that in a future edition. I would prefer if we would just do that, and call it a day. However, I imagine that a lot of code will want to update to avoid this warning, or be compatible with a future edition. This will require API breaking changes all over the place. We could either say, "such is life", or we could try to allow updating the signatures of the FFI declarations without breaking their callers. Iff that were something worth doing, then the idea I suggested of automatically borrowing the array according to the mutability of the C FFI foreign function declaration fixes the error message of my example, where it is incorrectly diagnosed that the array was moved. In a sense, this is what we are already doing when we transform moves of arrays into pointers when calling foreign functions, so it isn't really a type system change, but rather a bug fix to the implementation of the type system that we already have.
Is this not just a duplicate of ?
It's an accident that the ABI matches up, since in Rust is equivalent to in C++, i.e. in C (although we might not guarantee any of that). And then there's stuff like this, which I don't think we should have landed - the situation in question is impossible in C (since you always would have a wrapper ):
I think raw arrays should be marked as . arrays should be passed by ref/pointer. EDIT: Related: Will try to make a PR.

Discussion:
} impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { <ins> /// Check if the type is array and emit an unsafe type lint. fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { if let ty::Array(..) = ty.kind { self.emit_ffi_unsafe_type_lint( ty, sp, "passing raw arrays by value is not FFI-safe", Some("consider passing a pointer to the array"), ); true } else { false } } </ins> /// 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,

ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => self.check_type_for_ffi(cache, ty), <del> ty::Array(ty, _) => self.check_type_for_ffi(cache, ty), </del> <ins> ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty), </ins> ty::FnPtr(sig) => { match sig.abi() {

} } <del> fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) { </del> <ins> fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>, is_static: bool) { </ins> // We have to check for opaque types before `normalize_erasing_regions`, // which will replace opaque types with their underlying concrete type. if self.check_for_opaque_ty(sp, ty) {

// it is only OK to use this function because extern fns cannot have // any generic types right now: let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); <ins> // C doesn't really support passing arrays by value. // The only way to pass an array by value is through a struct. // So we first test that the top level isn't an array, // and then recursively check the types inside. if !is_static && self.check_for_array_ty(sp, ty) { return; } </ins> match self.check_type_for_ffi(&mut FxHashSet::default(), ty) { FfiResult::FfiSafe => {}

let sig = self.cx.tcx.erase_late_bound_regions(&sig); for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) { <del> self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty); </del> <ins> self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false); </ins> } if let hir::Return(ref ret_hir) = decl.output { let ret_ty = sig.output(); if !ret_ty.is_unit() { <del> self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty); </del> <ins> self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false); </ins> } } }

fn check_foreign_static(&mut self, id: hir::HirId, span: Span) { let def_id = self.cx.tcx.hir().local_def_id(id); let ty = self.cx.tcx.type_of(def_id); <del> self.check_type_for_ffi_and_report_errors(span, ty); </del> <ins> self.check_type_for_ffi_and_report_errors(span, ty, true); </ins> } }

pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128` pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str` pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box<u32>` <ins> pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]` pub static static_u128_type: u128; //~ ERROR: uses type `u128` pub static static_u128_array_type: [u128; 16]; //~ ERROR: uses type `u128` </ins> pub fn good3(fptr: Option<extern fn()>); pub fn good4(aptr: &[u8; 4 as usize]);

pub fn good17(p: TransparentCustomZst); #[allow(improper_ctypes)] pub fn good18(_: &String); <ins> pub fn good20(arr: *const [u8; 8]); pub static good21: [u8; 8]; </ins> } #[allow(improper_ctypes)]

= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout <del> error: aborting due to 20 previous errors </del> <ins> error: `extern` block uses type `[u8; 8]`, which is not FFI-safe --> $DIR/lint-ctypes.rs:68:27 | LL |     pub fn raw_array(arr: [u8; 8]); |                           ^^^^^^^ not FFI-safe | = help: consider passing a pointer to the array = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `u128`, which is not FFI-safe --> $DIR/lint-ctypes.rs:70:34 | LL |     pub static static_u128_type: u128; |                                  ^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe --> $DIR/lint-ctypes.rs:71:40 | LL |     pub static static_u128_array_type: [u128; 16]; |                                        ^^^^^^^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI error: aborting due to 23 previous errors </ins>