From 36c6b0a16050bec99fec3a7d8dff7a0a6b9f0c27 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 15 Jun 2023 15:06:55 -0700 Subject: [PATCH] Split out all layout rules into separate sections with examples. --- src/doc/src/reference/semver.md | 748 ++++++++++++++++++++++++++++++-- 1 file changed, 702 insertions(+), 46 deletions(-) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 96313f779c30..11a8d01dc2b4 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -214,7 +214,7 @@ crates should be avoided. It is a breaking change to change the alignment, layout, or size of a type that was previously well-defined. In general, types that use the [the default representation] do not have a well-defined alignment, layout, or size. -The compiler is free to alter the alignment or layout, so code should not make any assumptions about it. +The compiler is free to alter the alignment, layout or size, so code should not make any assumptions about it. > **Note**: It may be possible for external crates to break if they make assumptions about the alignment, layout, or size of a type even if it is not well-defined. > This is not considered a SemVer breaking change since those assumptions should not be made. @@ -222,97 +222,753 @@ The compiler is free to alter the alignment or layout, so code should not make a Some examples of changes that are not a breaking change are (assuming no other rules in this guide are violated): * Adding, removing, or changing fields of a default representation struct, union, or enum in such a way that the change follows the other rules in this guide (for example, using `non_exhaustive` to allow those changes, or changes to private fields that are already private). + See [struct-add-private-field-when-public](#struct-add-private-field-when-public), [struct-add-public-field-when-no-private](#struct-add-public-field-when-no-private), [struct-private-fields-with-private](#struct-private-fields-with-private), [enum-fields-new](#enum-fields-new). * Adding variants to a default representation enum, if the enum uses `non_exhaustive`. This may change the alignment or size of the enumeration, but those are not well-defined. -* Adding, removing, or changing private fields of a `repr(C)` struct, union, or enum. - Note that this may be a breaking change since it may change the size and alignment of the type. - Care should be taken in this case. - Adding private fields can only be done if there are already other private fields, or it is `non_exhaustive`. - Public fields may be added if there are private fields, or it is `non_exhaustive`, and the addition does not alter the layout of the other fields. + See [enum-variant-new](#enum-variant-new). +* Adding, removing, or changing private fields of a `repr(C)` struct, union, or enum, following the other rules in this guide (for example, using `non_exhaustive`, or adding private fields when other private fields already exist). + See [repr-c-private-change](#repr-c-private-change). * Adding variants to a `repr(C)` enum, if the enum uses `non_exhastive`. - Note that this may be a breaking change since it may change the size and alignment of the type. - Care should be taken in this case. + See [repr-c-enum-variant-new](#repr-c-enum-variant-new). * Adding `repr(C)` to a default representation struct, union, or enum. + See [repr-c-add](#repr-c-add). * Adding `repr()` [primitive representation] to an enum. + See [repr-int-enum-add](#repr-int-enum-add). * Adding `repr(transparent)` to a default representation struct or enum. + See [repr-transparent-add](#repr-transparent-add). Nominal types that use the [`repr` attribute] can be said to have an alignment and layout that is defined in some way that code may make some assumptions about that may break as a result of changing that type. Some examples of a breaking change are: * Adding `repr(packed)` to a struct or union. + See [repr-packed-add](#repr-packed-add). +* Adding `repr(align)` to a struct, union, or enum. + See [repr-align-add](#repr-align-add). +* Removing `repr(packed)` from a struct or union. + See [repr-packed-remove](#repr-packed-remove). +* Changing the value N of `repr(packed(N))` if that changes the alignment or layout. + See [repr-packed-n-change](#repr-packed-n-change). +* Changing the value N of `repr(align(N))` if that changes the alignment. + See [repr-align-n-change](#repr-align-n-change). +* Removing `repr(align)` from a struct, union, or enum. + See [repr-align-remove](#repr-align-remove). +* Changing the order of public fields of a `repr(C)` type. + See [repr-c-shuffle](#repr-c-shuffle). +* Removing `repr(C)` from a struct, union, or enum. + See [repr-c-remove](#repr-c-remove). +* Removing `repr()` from an enum. + See [repr-int-enum-remove](#repr-int-enum-remove). +* Changing the primitive representation of a `repr()` enum. + See [repr-int-enum-change](#repr-int-enum-change). +* Removing `repr(transparent)` from a struct or enum. + See [repr-transparent-remove](#repr-transparent-remove). - Making a type `repr(packed)` makes changes that can break code, such as being invalid to take a reference to a field, or causing truncation of disjoint closure captures. +In some cases, types with a `repr` attribute may not have an alignment, layout, or size that is well-defined. +In these cases, it may be safe to make changes to the types, though care should be exercised. +For example, types with private fields that do not otherwise document their alignment, layout, or size guarantees cannot be relied upon by external crates since the public API does not fully define the alignment, layout, or size of the type. - +A common example where a type with *private* fields is well-defined is a type with a single private field with a generic type, using `repr(transparent)`, and which is documented as being transparent to the generic type. +For example, see [`UnsafeCell`]. -* Adding `repr(align)` to a struct, union, or enum. +[the default representation]: ../../reference/type-layout.html#the-default-representation +[primitive representation]: ../../reference/type-layout.html#primitive-representations +[`repr` attribute]: ../../reference/type-layout.html#representations +[`std::mem::transmute`]: ../../std/mem/fn.transmute.html +[`UnsafeCell`]: ../../std/cell/struct.UnsafeCell.html#memory-layout - Making a type `repr(align)` would break any use of that type in a `repr(packed)` type because that combination is not allowed. + +#### Minor: `repr(C)` add, remove, or change a private field - +It is usually safe to add, remove, or change a private field of a `repr(C)` struct, union, or enum, assuming it follows the other guidelines in this guide (see [struct-add-private-field-when-public](#struct-add-private-field-when-public), [struct-add-public-field-when-no-private](#struct-add-public-field-when-no-private), [struct-private-fields-with-private](#struct-private-fields-with-private), [enum-fields-new](#enum-fields-new)). -* Removing `repr(packed)` from a struct or union. +For example, adding private fields can only be done if there are already other private fields, or it is `non_exhaustive`. +Public fields may be added if there are private fields, or it is `non_exhaustive`, and the addition does not alter the layout of the other fields. - This may change the alignment or layout that extern crates are relying on. +However, this may change the size and alignment of the type. +Care should be taken if the size or alignment changes. +Code should not make assumptions about the size or alignment of types with private fields or `non_exhaustive` unless it has a documented size or alignment. - If any fields are public, then removing `repr(packed)` may change the way disjoint closure captures work. - In some cases, this can cause code to break, similar to those outlined in the [edition guide][edition-closures]. +```rust,ignore +// MINOR CHANGE -* Changing the value N of `repr(packed(N))` if that changes the alignment or layout. +/////////////////////////////////////////////////////////// +// Before +#[derive(Default)] +#[repr(C)] +pub struct Example { + pub f1: i32, + f2: i32, // a private field +} - This may change the alignment or layout that external crates are relying on. +/////////////////////////////////////////////////////////// +// After +#[derive(Default)] +#[repr(C)] +pub struct Example { + pub f1: i32, + f2: i32, + f3: i32, // a new field +} - If the value N is lowered below the alignment of a public field, then that would break any code that attempts to take a reference of that field. +/////////////////////////////////////////////////////////// +// Example use of the library that will safely work. +fn main() { + // NOTE: Users should not make assumptions about the size or alignment + // since they are not documented. + let f = updated_crate::Example::default(); +} +``` -* Changing the value N of `repr(align(N))` if that changes the alignment. + +#### Minor: `repr(C)` add enum variant - This may change the alignment that external crates are relying on. +It is usually safe to add variants to a `repr(C)` enum, if the enum uses `non_exhastive`. +See [enum-variant-new](#enum-variant-new) for more discussion. - This change should be safe to make if the type is not well-defined as discussed below (such as having any private fields and having an undocumented alignment or layout). +Note that this may be a breaking change since it changes the size and alignment of the type. +See [repr-c-private-change](#repr-c-private-change) for similar concerns. -* Removing `repr(align)` from a struct, union, or enum. +```rust,ignore +// MINOR CHANGE - This may change the alignment or layout that external crates are relying on. +/////////////////////////////////////////////////////////// +// Before +#[repr(C)] +#[non_exhaustive] +pub enum Example { + Variant1 { f1: i16 }, + Variant2 { f1: i32 }, +} - This change should be safe to make if the type is not well-defined as discussed below (such as having any private fields and having an undocumented alignment). +/////////////////////////////////////////////////////////// +// After +#[repr(C)] +#[non_exhaustive] +pub enum Example { + Variant1 { f1: i16 }, + Variant2 { f1: i32 }, + Variant3 { f1: i64 }, // added +} -* Changing the order of public fields of a `repr(C)` type. +/////////////////////////////////////////////////////////// +// Example use of the library that will safely work. +fn main() { + // NOTE: Users should not make assumptions about the size or alignment + // since they are not specified. For example, this raised the size from 8 + // to 16 bytes. + let f = updated_crate::Example::Variant2 { f1: 123 }; +} +``` - External crates may be relying on the specific ordering of the fields. + +#### Minor: Adding `repr(C)` to a default representation -* Removing `repr(C)` from a struct, union, or enum. +It is safe to add `repr(C)` to a struct, union, or enum with [the default representation]. +This is safe because users should not make assumptions about the alignment, layout, or size of types with with the default representation. - External crates may be relying on the specific layout of the type. +```rust,ignore +// MINOR CHANGE -* Removing `repr()` from an enum. +/////////////////////////////////////////////////////////// +// Before +pub struct Example { + pub f1: i32, + pub f2: i16, +} - External crates may be assuming that the discriminant is a specific size. +/////////////////////////////////////////////////////////// +// After +#[repr(C)] // added +pub struct Example { + pub f1: i32, + pub f2: i16, +} - For example, [`std::mem::transmute`] of an enum may fail. +/////////////////////////////////////////////////////////// +// Example use of the library that will safely work. +fn main() { + let f = updated_crate::Example { f1: 123, f2: 456 }; +} +``` -* Changing the primitive representation of a `repr()` enum. + +#### Minor: Adding `repr()` to an enum - External crates may be assuming that the discriminant is a specific size. +It is safe to add `repr()` [primitive representation] to an enum with [the default representation]. +This is safe because users should not make assumptions about the alignment, layout, or size of an enum with the default representation. - For example, [`std::mem::transmute`] of an enum may fail. +```rust,ignore +// MINOR CHANGE -* Removing `repr(transparent)` from a struct or enum. +/////////////////////////////////////////////////////////// +// Before +pub enum E { + Variant1, + Variant2(i32), + Variant3 { f1: f64 }, +} - External crates may be relying on the type having the alignment, layout, or size of the transparent field. +/////////////////////////////////////////////////////////// +// After +#[repr(i32)] // added +pub enum E { + Variant1, + Variant2(i32), + Variant3 { f1: f64 }, +} -In some cases, types with a `repr` attribute may not have an alignment, layout, or size that is well-defined. -In these cases, it may be safe to make changes to the types, though care should be exercised. -For example, types with private fields that do not otherwise document their alignment, layout, or size guarantees cannot be relied upon by external crates since the public API does not fully define the alignment, layout, or size of the type. +/////////////////////////////////////////////////////////// +// Example use of the library that will safely work. +fn main() { + let x = updated_crate::E::Variant3 { f1: 1.23 }; +} +``` -A common example where a type with *private* fields is well-defined is a type with a single private field with a generic type, using `repr(transparent)`, and which is documented as being transparent to the generic type. + +#### Minor: Adding `repr(transparent)` to a default representation struct or enum +It is safe to add `repr(transparent)` to a struct or enum with [the default representation]. +This is safe because users should not make assumptions about the alignment, layout, or size of a struct or enum with the default representation. + +```rust,ignore +// MINOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[derive(Default)] +pub struct Example(T); + +/////////////////////////////////////////////////////////// +// After +#[derive(Default)] +#[repr(transparent)] // added +pub struct Example(T); + +/////////////////////////////////////////////////////////// +// Example use of the library that will safely work. +fn main() { + let x = updated_crate::Example::::default(); +} +``` + + +#### Major: Adding `repr(packed)` to a struct or union + +It is a breaking change to add `repr(packed)` to a struct or union. +Making a type `repr(packed)` makes changes that can break code, such as being invalid to take a reference to a field, or causing truncation of disjoint closure captures. + + + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +pub struct Example { + pub f1: u8, + pub f2: u16, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(packed)] // added +pub struct Example { + pub f1: u8, + pub f2: u16, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +fn main() { + let f = updated_crate::Example { f1: 1, f2: 2 }; + let x = &f.f2; // Error: reference to packed field is unaligned +} +``` + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +pub struct Example(pub i32, pub i32); + +/////////////////////////////////////////////////////////// +// After +#[repr(packed)] +pub struct Example(pub i32, pub i32); + +/////////////////////////////////////////////////////////// +// Example usage that will break. +fn main() { + let mut f = updated_crate::Example(123, 456); + let c = || { + // Without repr(packed), the closure precisely captures `&f.0`. + // With repr(packed), the closure captures `&f` to avoid undefined behavior. + let a = f.0; + }; + f.1 = 789; // Error: cannot assign to `f.1` because it is borrowed + c(); +} +``` + + +#### Major: Adding `repr(align)` to a struct, union, or enum + +It is a breaking change to add `repr(align)` to a struct, union, or enum. +Making a type `repr(align)` would break any use of that type in a `repr(packed)` type because that combination is not allowed. + + + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +pub struct Aligned { + pub a: i32, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(align(8))] // added +pub struct Aligned { + pub a: i32, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::Aligned; + +#[repr(packed)] +pub struct Packed { // Error: packed type cannot transitively contain a `#[repr(align)]` type + f1: Aligned, +} + +fn main() { + let p = Packed { + f1: Aligned { a: 123 }, + }; +} +``` + + +#### Major: Removing `repr(packed)` from a struct or union + +It is a breaking change to remove `repr(packed)` from a struct or union. +This may change the alignment or layout that extern crates are relying on. + +If any fields are public, then removing `repr(packed)` may change the way disjoint closure captures work. +In some cases, this can cause code to break, similar to those outlined in the [edition guide][edition-closures]. -[the default representation]: ../../reference/type-layout.html#the-default-representation -[primitive representation]: ../../reference/type-layout.html#primitive-representations -[`repr` attribute]: ../../reference/type-layout.html#representations [edition-closures]: ../../edition-guide/rust-2021/disjoint-capture-in-closures.html -[`std::mem::transmute`]: ../../std/mem/fn.transmute.html + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(C, packed)] +pub struct Packed { + pub a: u8, + pub b: u16, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(C)] // removed packed +pub struct Packed { + pub a: u8, + pub b: u16, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::Packed; + +fn main() { + let p = Packed { a: 1, b: 2 }; + // Some assumption about the size of the type. + // Without `packed`, this fails since the size is 4. + const _: () = assert!(std::mem::size_of::() == 3); // Error: evaluation of constant value failed +} +``` + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(C, packed)] +pub struct Packed { + pub a: *mut i32, + pub b: i32, +} +unsafe impl Send for Packed {} + +/////////////////////////////////////////////////////////// +// After +#[repr(C)] // removed packed +pub struct Packed { + pub a: *mut i32, + pub b: i32, +} +unsafe impl Send for Packed {} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::Packed; + +fn main() { + let mut x = 123; + + let p = Packed { + a: &mut x as *mut i32, + b: 456, + }; + + // When the structure was packed, the closure captures `p` which is Send. + // When `packed` is removed, this ends up capturing `p.a` which is not Send. + std::thread::spawn(move || unsafe { + *(p.a) += 1; // Error: cannot be sent between threads safely + }); +} +``` + + +#### Major: Changing the value N of `repr(packed(N))` if that changes the alignment or layout + +It is a breaking change to change the value of N of `repr(packed(N))` if that changes the alignment or layout. +This may change the alignment or layout that external crates are relying on. + +If the value `N` is lowered below the alignment of a public field, then that would break any code that attempts to take a reference of that field. + +Note that some changes to `N` may not change the alignment or layout, for example increasing it when the current value is already equal to the natural alignment of the type. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(packed(4))] +pub struct Packed { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(packed(2))] // changed to 2 +pub struct Packed { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::Packed; + +fn main() { + let p = Packed { a: 1, b: 2 }; + let x = &p.b; // Error: reference to packed field is unaligned +} +``` + + +#### Major: Changing the value N of `repr(align(N))` if that changes the alignment + +It is a breaking change to change the value `N` of `repr(align(N))` if that changes the alignment. +This may change the alignment that external crates are relying on. + +This change should be safe to make if the type is not well-defined as discussed in [type layout](#type-layout) (such as having any private fields and having an undocumented alignment or layout). + +Note that some changes to `N` may not change the alignment or layout, for example decreasing it when the current value is already equal to or less than the natural alignment of the type. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(align(8))] +pub struct Packed { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(align(4))] // changed to 4 +pub struct Packed { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::Packed; + +fn main() { + let p = Packed { a: 1, b: 2 }; + // Some assumption about the size of the type. + // The alignment has changed from 8 to 4. + const _: () = assert!(std::mem::align_of::() == 8); // Error: evaluation of constant value failed +} +``` + + +#### Major: Removing `repr(align)` from a struct, union, or enum + +It is a breaking change to remove `repr(align)` from a struct, union, or enum. +This may change the alignment or layout that external crates are relying on. + +This change should be safe to make if the type is not well-defined as discussed in [type layout](#type-layout) (such as having any private fields and having an undocumented alignment). + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(C, align(8))] +pub struct Packed { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(C)] // removed align +pub struct Packed { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::Packed; + +fn main() { + let p = Packed { a: 1, b: 2 }; + // Some assumption about the size of the type. + // The alignment has changed from 8 to 4. + const _: () = assert!(std::mem::align_of::() == 8); // Error: evaluation of constant value failed +} +``` + + +#### Major: Changing the order of public fields of a `repr(C)` type + +It is a breaking change to change the order of public fields of a `repr(C)` type. +External crates may be relying on the specific ordering of the fields. + +```rust,ignore,run-fail +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(C)] +pub struct SpecificLayout { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(C)] +pub struct SpecificLayout { + pub b: u32, // changed order + pub a: u8, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::SpecificLayout; + +extern "C" { + // This C function is assuming a specific layout defined in a C header. + fn c_fn_get_b(x: &SpecificLayout) -> u32; +} + +fn main() { + let p = SpecificLayout { a: 1, b: 2 }; + unsafe { assert_eq!(c_fn_get_b(&p), 2) } // Error: value not equal to 2 +} + +# mod cdep { +# // This simulates what would normally be something included from a build script. +# // This definition would be in a C header. +# #[repr(C)] +# pub struct SpecificLayout { +# pub a: u8, +# pub b: u32, +# } +# +# #[no_mangle] +# pub fn c_fn_get_b(x: &SpecificLayout) -> u32 { +# x.b +# } +# } +``` + + +#### Major: Removing `repr(C)` from a struct, union, or enum + +It is a breaking change to remove `repr(C)` from a struct, union, or enum. +External crates may be relying on the specific layout of the type. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(C)] +pub struct SpecificLayout { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// After +// removed repr(C) +pub struct SpecificLayout { + pub a: u8, + pub b: u32, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::SpecificLayout; + +extern "C" { + // This C function is assuming a specific layout defined in a C header. + fn c_fn_get_b(x: &SpecificLayout) -> u32; // Error: is not FFI-safe +} + +fn main() { + let p = SpecificLayout { a: 1, b: 2 }; + unsafe { assert_eq!(c_fn_get_b(&p), 2) } +} + +# mod cdep { +# // This simulates what would normally be something included from a build script. +# // This definition would be in a C header. +# #[repr(C)] +# pub struct SpecificLayout { +# pub a: u8, +# pub b: u32, +# } +# +# #[no_mangle] +# pub fn c_fn_get_b(x: &SpecificLayout) -> u32 { +# x.b +# } +# } +``` + + +#### Major: Removing `repr()` from an enum + +It is a breaking change to remove `repr()` from an enum. +External crates may be assuming that the discriminant is a specific size. +For example, [`std::mem::transmute`] of an enum may fail. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(u16)] +pub enum Example { + Variant1, + Variant2, + Variant3, +} + +/////////////////////////////////////////////////////////// +// After +// removed repr(u16) +pub enum Example { + Variant1, + Variant2, + Variant3, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. + +fn main() { + let e = updated_crate::Example::Variant2; + let i: u16 = unsafe { std::mem::transmute(e) }; // Error: cannot transmute between types of different sizes +} +``` + + +#### Major: Changing the primitive representation of a `repr()` enum + +It is a breaking change to change the primitive representation of a `repr()` enum. +External crates may be assuming that the discriminant is a specific size. +For example, [`std::mem::transmute`] of an enum may fail. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(u16)] +pub enum Example { + Variant1, + Variant2, + Variant3, +} + +/////////////////////////////////////////////////////////// +// After +#[repr(u8)] // changed repr size +pub enum Example { + Variant1, + Variant2, + Variant3, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. + +fn main() { + let e = updated_crate::Example::Variant2; + let i: u16 = unsafe { std::mem::transmute(e) }; // Error: cannot transmute between types of different sizes +} +``` + + +#### Major: Removing `repr(transparent)` from a struct or enum + +It is a breaking change to remove `repr(transparent)` from a struct or enum. +External crates may be relying on the type having the alignment, layout, or size of the transparent field. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(transparent)] +pub struct Transparent(T); + +/////////////////////////////////////////////////////////// +// After +// removed repr +pub struct Transparent(T); + +/////////////////////////////////////////////////////////// +// Example usage that will break. +#![deny(improper_ctypes)] +use updated_crate::Transparent; + +extern "C" { + fn c_fn() -> Transparent; // Error: is not FFI-safe +} + +fn main() {} +``` + ### Major: adding a private struct field when all current fields are public