Code change:
) -> FfiResult<'tcx> { use FfiResult::*; <del> let transparent_safety = def.repr().transparent().then(|| { // Can assume that at most one field is not a ZST, so only check // that field's type for FFI-safety. </del> <ins> let transparent_with_all_zst_fields = if def.repr().transparent() { </ins> if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { <del> return self.check_field_type_for_ffi(cache, field, args); </del> <ins> // Transparent newtypes have at most one non-ZST field which needs to be checked.. match self.check_field_type_for_ffi(cache, field, args) { FfiUnsafe { ty, .. } if ty.is_unit() => (), r => return r, } false </ins> } else { <del> // All fields are ZSTs; this means that the type should behave // like (), which is FFI-unsafe... except if all fields are PhantomData, // which is tested for below FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } </del> <ins> // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all // `PhantomData`). true </ins> } <del> }); // We can't completely trust repr(C) markings; make sure the fields are // actually safe. </del> <ins> } else { false }; // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. </ins> let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { <del> match self.check_field_type_for_ffi(cache, &field, args) { FfiSafe => { all_phantom = false; } FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => { return FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_enum_phantomdata, help: None, }; } FfiPhantom(..) => {} r => return transparent_safety.unwrap_or(r), </del> <ins> all_phantom &= match self.check_field_type_for_ffi(cache, &field, args) { FfiSafe => false, // `()` fields are FFI-safe! FfiUnsafe { ty, .. } if ty.is_unit() => false, FfiPhantom(..) => true, r @ FfiUnsafe { .. } => return r, </ins> } } <del> if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) } </del> <ins> if all_phantom { FfiPhantom(ty) } else if transparent_with_all_zst_fields { FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } } else { FfiSafe } </ins> } /// Checks if the given type is "ffi-safe" (has a stable, well-defined

Related discussion:
text: The function itself is considered FFI-safe and does not trigger the warning (). Its function pointer should also be allowed, to be consistent. Discovered on CI The original code has several more levels of indirection, but is also considered FFI-safe before. <details<summaryExtracted original code</summary</detailsAccording to CI logs, the warning occurs since rust 1.72.0-nightly ( 2023-07-03). Probably caused by . cc
Okay, so, I had a fix for this that just got rid of the regression, but I'm not sure that's the correct solution here - I think this is correct to be linting in the fn-ptr case and should be linting for the function case. is FFI-unsafe, but we allow a function to return because that's the default, but that doesn't mean that a within a type is okay (with the exception of a wrapper around the - because that's just returning , which we've already decided is okay). In the function case that works right now, that's being allowed because of this code.. ..which is there to support the case. However, it's not quite the right check. It doesn't actually check for ! It checks that the type - however nested - which was deemed FFI-unsafe is and that type is part of a return type. So any FFI-unsafe in a return type makes the entire return type FFI-safe - which is clearly incorrect. That's making the example below compile, which absolutely shouldn't. I've submitted , which makes this even more of a regression by making the function case fail too. We already had a simple "is it a unit" check, so I extended that to check for transparent newtype wrappers too, and removed the incorrect check.
This is unfortunate since I'm trying to return a where is likely to be . What should I do instead if I want to be FFI-safe with layout ?
I'm not so sure anymore after reading comment (). It's clear that some changes are required regardless - our current implementation wrong as demonstrated in the final example from my previous comment, but it's also inconsistent, in that this example... lint for the argument use of , but not the return use of , when really it should be both or neither. After my pull request, it's both.