-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #82878 - sexxi-goose:repr_packed, r=nikomatsakis
2229: Handle capturing a reference into a repr packed struct RFC 1240 states that it is unsafe to capture references into a packed-struct. This PR ensures that when a closure captures a precise path, we aren't violating this safety constraint. To acheive so we restrict the capture precision to the struct itself. An interesting edge case where we decided to restrict precision: ```rust struct Foo(String); let foo: Foo; let c = || { println!("{}", foo.0); let x = foo.0; } ``` Given how closures get desugared today, foo.0 will be moved into the closure, making the `println!`, safe. However this can be very subtle and also will be unsafe if the closure gets inline. Closes: rust-lang/project-rfc-2229#33 r? `@nikomatsakis`
- Loading branch information
Showing
5 changed files
with
381 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// check-pass | ||
|
||
#![feature(capture_disjoint_fields)] | ||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete | ||
|
||
// Given how the closure desugaring is implemented (at least at the time of writing this test), | ||
// we don't need to truncate the captured path to a reference into a packed-struct if the field | ||
// being referenced will be moved into the closure, since it's safe to move out a field from a | ||
// packed-struct. | ||
// | ||
// However to avoid surprises for the user, or issues when the closure is | ||
// inlined we will truncate the capture to access just the struct regardless of if the field | ||
// might get moved into the closure. | ||
// | ||
// It is possible for someone to try writing the code that relies on the desugaring to access a ref | ||
// into a packed-struct without explicity using unsafe. Here we test that the compiler warns the | ||
// user that such an access is still unsafe. | ||
fn test_missing_unsafe_warning_on_repr_packed() { | ||
#[repr(packed)] | ||
struct Foo { x: String } | ||
|
||
let foo = Foo { x: String::new() }; | ||
|
||
let c = || { | ||
println!("{}", foo.x); | ||
//~^ WARNING: borrow of packed field is unsafe and requires unsafe function or block | ||
//~| WARNING: this was previously accepted by the compiler but is being phased out | ||
let _z = foo.x; | ||
}; | ||
|
||
c(); | ||
} | ||
|
||
fn main() { | ||
test_missing_unsafe_warning_on_repr_packed(); | ||
} |
22 changes: 22 additions & 0 deletions
22
src/test/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes | ||
--> $DIR/repr_packed.rs:3:12 | ||
| | ||
LL | #![feature(capture_disjoint_fields)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= note: `#[warn(incomplete_features)]` on by default | ||
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information | ||
|
||
warning: borrow of packed field is unsafe and requires unsafe function or block (error E0133) | ||
--> $DIR/repr_packed.rs:25:24 | ||
| | ||
LL | println!("{}", foo.x); | ||
| ^^^^^ | ||
| | ||
= note: `#[warn(safe_packed_borrows)]` on by default | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! | ||
= note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> | ||
= note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior | ||
|
||
warning: 2 warnings emitted | ||
|
103 changes: 103 additions & 0 deletions
103
src/test/ui/closures/2229_closure_analysis/repr_packed.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
#![feature(capture_disjoint_fields)] | ||
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete | ||
//~| `#[warn(incomplete_features)]` on by default | ||
//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488> | ||
|
||
#![feature(rustc_attrs)] | ||
|
||
// `u8` aligned at a byte and are unaffected by repr(packed). | ||
// Therefore we can precisely (and safely) capture references to both the fields. | ||
fn test_alignment_not_affected() { | ||
#[repr(packed)] | ||
struct Foo { x: u8, y: u8 } | ||
|
||
let mut foo = Foo { x: 0, y: 0 }; | ||
|
||
let mut c = #[rustc_capture_analysis] | ||
//~^ ERROR: attributes on expressions are experimental | ||
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> | ||
|| { | ||
//~^ ERROR: First Pass analysis includes: | ||
//~| ERROR: Min Capture analysis includes: | ||
let z1: &u8 = &foo.x; | ||
//~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow | ||
//~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow | ||
let z2: &mut u8 = &mut foo.y; | ||
//~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow | ||
//~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow | ||
|
||
*z2 = 42; | ||
|
||
println!("({}, {})", z1, z2); | ||
}; | ||
|
||
c(); | ||
} | ||
|
||
// `String`, `u16` are not aligned at a one byte boundry and are thus affected by repr(packed). | ||
// | ||
// Here we test that the closure doesn't capture a reference point to `foo.x` but | ||
// rather capture `foo` entirely. | ||
fn test_alignment_affected() { | ||
#[repr(packed)] | ||
struct Foo { x: String, y: u16 } | ||
|
||
let mut foo = Foo { x: String::new(), y: 0 }; | ||
|
||
let mut c = #[rustc_capture_analysis] | ||
//~^ ERROR: attributes on expressions are experimental | ||
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> | ||
|| { | ||
//~^ ERROR: First Pass analysis includes: | ||
//~| ERROR: Min Capture analysis includes: | ||
let z1: &String = &foo.x; | ||
let z2: &mut u16 = &mut foo.y; | ||
//~^ NOTE: Capturing foo[] -> MutBorrow | ||
//~| NOTE: Min Capture foo[] -> MutBorrow | ||
|
||
|
||
*z2 = 42; | ||
|
||
println!("({}, {})", z1, z2); | ||
}; | ||
|
||
c(); | ||
} | ||
|
||
// Given how the closure desugaring is implemented (at least at the time of writing this test), | ||
// we don't need to truncate the captured path to a reference into a packed-struct if the field | ||
// being referenced will be moved into the closure, since it's safe to move out a field from a | ||
// packed-struct. | ||
// | ||
// However to avoid surprises for the user, or issues when the closure is | ||
// inlined we will truncate the capture to access just the struct regardless of if the field | ||
// might get moved into the closure. | ||
fn test_truncation_when_ref_and_move() { | ||
#[repr(packed)] | ||
struct Foo { x: String } | ||
|
||
let mut foo = Foo { x: String::new() }; | ||
|
||
let c = #[rustc_capture_analysis] | ||
//~^ ERROR: attributes on expressions are experimental | ||
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> | ||
|| { | ||
//~^ ERROR: First Pass analysis includes: | ||
//~| ERROR: Min Capture analysis includes: | ||
println!("{}", foo.x); | ||
//~^ NOTE: Capturing foo[] -> ImmBorrow | ||
//~| NOTE: Min Capture foo[] -> ByValue | ||
//~| NOTE: foo[] used here | ||
let _z = foo.x; | ||
//~^ NOTE: Capturing foo[(0, 0)] -> ByValue | ||
//~| NOTE: foo[] captured as ByValue here | ||
}; | ||
|
||
c(); | ||
} | ||
|
||
fn main() { | ||
test_truncation_when_ref_and_move(); | ||
test_alignment_affected(); | ||
test_alignment_not_affected(); | ||
} |
Oops, something went wrong.