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

Discussion:
If I have a wrapper around a , it's considered an improper C type when used in a struct field, even though the wrapped would be allowed. Specifically () produces the error This is over-zealous for a few reasons. on its own is allowed in C structs, so changing this to would solve the warning (but defeats the point of my use case[^1]). is supposed to be treated as the inner type for purposes of ABI, and the lint is about ABI. instead of solves this technically (it shuts the warning up), but seems pretty dodgy, since it's not actually laid out the way C would lay it out anymore (that is, we've guaranteed that is never going to impact layout at all, and we haven't guaranteed this for zero-sized types which are notably not a real thing in C). TLDR: this lint fires on wrappers around when they're used as struct fields. IMO these should be fine since doesn't emit a warning in that case, and is supposed to defer to the inner type. . It's important that the type not be a direct (basically is not really usable for FFI, but C has cases where considers appending new fields to the end of a type to be a non-breaking change). (That said, firing this lint on a type passed by pointer is pretty weird anyway, since the indirection means doesn't actually have to be a valid C type (for the ABI), which is the basic complaint behind , which is not what I'm complaining about. IMO this shouldn't fire even if were being passed by value, as making the field (instead of a wrapper around one) doesn't fire in that case) <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOT_END --
I'd like to take a look at this. claim
Related: