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: With and without my pull request, when you change this to , it doesn't lint.
I'll nominate this issue so that it sees some discussion - see also and .
I posted some more info on zulip but it may also worth mentioning here. ZST is still zero-sized under repr(C), rather than FFI-unsafe.
The issue now also exists in beta. I also found uitests which seems to expect ZST to be FFI-safe, but ends in to suppress the warning and tests nothing.
We discussed this in today's meeting. We didn't have consensus that this should warn; at least some people thought the code in the top comment should continue to compile without warnings. We did have consensus that changing that (to warn) would need an FCP. So, we felt that we'd like to see a fix removing the warning applied to beta and nightly, and then separately if someone wants to propose that we change this to warn, we could evaluate that and do an FCP on it. But at the moment the temperature of the team seems to be that we don't have consensus for such an FCP.
WG-prioritization assigning priority (). label -I-prioritize +P-medium
labels -I-lang-nominated We rendered our opinion