diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 0404efdcfb2..652a1b6133c 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -62,11 +62,13 @@ considered incompatible. * Structs * [Major: adding a private struct field when all current fields are public](#struct-add-private-field-when-public) * [Major: adding a public field when no private field exists](#struct-add-public-field-when-no-private) + * [Major: change a from well-defined repr to another when all fields are public](#struct-change-repr-when-public) * [Minor: adding or removing private fields when at least one already exists](#struct-private-fields-with-private) * [Minor: going from a tuple struct with all private fields (with at least one field) to a normal struct, or vice versa](#struct-tuple-normal-with-private) * Enums * [Major: adding new enum variants (without `non_exhaustive`)](#enum-variant-new) * [Major: adding new fields to an enum variant](#enum-fields-new) + * [Major: removing an integer repr](#enum-remove-integer-repr) * Traits * [Major: adding a non-defaulted trait item](#trait-new-item-no-default) * [Major: any change to trait item signatures](#trait-item-signature) @@ -273,6 +275,58 @@ Mitigation strategies: a struct to prevent users from using struct literal syntax, and instead provide a constructor method and/or [Default] implementation. + +### Major: change a from well-defined repr to another when all fields are public + +When a struct has a well-defined repr (`transparent`, `C`) and only public fields, this will break unsafe code relying on +that repr. + +Adding a well-defined repr to a `repr(Rust)` struct is *not* a breaking change. + +```rust,ignore,run-fail +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(C)] +pub struct Foo { + pub f1: u8, + pub f2: u16, + pub f3: u8, +} + +/////////////////////////////////////////////////////////// +// After +pub struct Foo { + pub f1: u8, + pub f2: u16, + pub f3: u8, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::Foo; + +fn main() { + let foo = Foo { + f1: 1, + f2: 1000, + f3: 5, + }; + + let foo_ptr = &foo as *const Foo as *const u8; + + // this is now unsound because of the change + // SAFETY: Foo is repr(C), so we are guaranteed that there will be `3` at this offset (u8, 8 pad, u16) + let f3 = unsafe { foo_ptr.offset(4).read() }; + + assert_eq!(5, f3); +} +``` + +Mitigation strategies: +* Only add a `repr` to structs with public fields if you don't plan to change it again. + ### Minor: adding or removing private fields when at least one already exists @@ -453,6 +507,46 @@ Mitigation strategies: Variant1(Foo) } ``` + + +### Major: removing an integer repr + +It is a breaking change to remove an integer repr (like `#[repr(u8)]`) from an enum. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +#[repr(i32)] +pub enum Number { + One = 1, + Two = 2, + Three = 3, +} + +/////////////////////////////////////////////////////////// +// After +pub enum Number { + One = 1, + Two = 2, + Three = 3, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +fn main() { + let num_three = updated_crate::Number::Three; + + // SAFETY: `Number` is `#[repr(i32)]` + let three: i32 = unsafe { std::mem::transmute(num_three) }; // Error: cannot transmute between types of different sizes + + assert_eq!(3, three) +} +``` + +Mitigation strategies: +* Only add `repr` to an enum if you are sure you'll never need to remove it ### Major: adding a non-defaulted trait item