|  | //@ test-mir-pass: ScalarReplacementOfAggregates | 
|  | //@ compile-flags: -Cpanic=abort | 
|  | //@ no-prefer-dynamic | 
|  |  | 
|  | struct Tag(usize); | 
|  |  | 
|  | #[repr(C)] | 
|  | struct S(Tag, Tag, Tag); | 
|  |  | 
|  | impl Drop for Tag { | 
|  | #[inline(never)] | 
|  | fn drop(&mut self) {} | 
|  | } | 
|  |  | 
|  | /// Check that SROA excludes structs with a `Drop` implementation. | 
|  | pub fn dropping() { | 
|  | // CHECK-LABEL: fn dropping( | 
|  |  | 
|  | // CHECK: [[aggregate:_[0-9]+]]: S; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK: [[aggregate]] = S | 
|  | S(Tag(0), Tag(1), Tag(2)).1; | 
|  | } | 
|  |  | 
|  | /// Check that SROA excludes enums. | 
|  | pub fn enums(a: usize) -> usize { | 
|  | // CHECK-LABEL: fn enums( | 
|  |  | 
|  | // CHECK: [[enum:_[0-9]+]]: std::option::Option<usize>; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK: [[enum]] = Option::<usize>::Some | 
|  | // CHECK: _5 = copy (([[enum]] as Some).0: usize) | 
|  | // CHECK: _0 = copy _5 | 
|  | if let Some(a) = Some(a) { a } else { 0 } | 
|  | } | 
|  |  | 
|  | /// Check that SROA destructures `U`. | 
|  | pub fn structs(a: f32) -> f32 { | 
|  | // CHECK-LABEL: fn structs( | 
|  | struct U { | 
|  | _foo: usize, | 
|  | a: f32, | 
|  | } | 
|  | // CHECK: [[ret:_0]]: f32; | 
|  | // CHECK: [[struct:_[0-9]+]]: structs::U; | 
|  | // CHECK: [[a_tmp:_[0-9]+]]: f32; | 
|  | // CHECK: [[foo:_[0-9]+]]: usize; | 
|  | // CHECK: [[a_ret:_[0-9]+]]: f32; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK-NOT: [[struct]] | 
|  | // CHECK: [[a_tmp]] = copy _1; | 
|  | // CHECK-NOT: [[struct]] | 
|  | // CHECK: [[foo]] = const 0_usize; | 
|  | // CHECK-NOT: [[struct]] | 
|  | // CHECK: [[a_ret]] = move [[a_tmp]]; | 
|  | // CHECK-NOT: [[struct]] | 
|  | // CHECK: _0 = copy [[a_ret]]; | 
|  | // CHECK-NOT: [[struct]] | 
|  | U { _foo: 0, a }.a | 
|  | } | 
|  |  | 
|  | /// Check that SROA excludes unions. | 
|  | pub fn unions(a: f32) -> u32 { | 
|  | // CHECK-LABEL: fn unions( | 
|  | union Repr { | 
|  | f: f32, | 
|  | u: u32, | 
|  | } | 
|  | // CHECK: [[union:_[0-9]+]]: unions::Repr; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK: [[union]] = Repr { | 
|  | // CHECK: _0 = copy ([[union]].1: u32) | 
|  | unsafe { Repr { f: a }.u } | 
|  | } | 
|  |  | 
|  | #[derive(Copy, Clone)] | 
|  | struct Foo { | 
|  | a: u8, | 
|  | b: (), | 
|  | c: &'static str, | 
|  | d: Option<isize>, | 
|  | } | 
|  |  | 
|  | /// Check that non-escaping uses of a struct are destructured. | 
|  | pub fn flat() { | 
|  | // CHECK-LABEL: fn flat( | 
|  |  | 
|  | // CHECK: [[struct:_[0-9]+]]: Foo; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK: [[init_unit:_[0-9]+]] = (); | 
|  | // CHECK: [[init_opt_isize:_[0-9]+]] = Option::<isize>::Some | 
|  |  | 
|  | // CHECK: [[destr_five:_[0-9]+]] = const 5_u8; | 
|  | // CHECK: [[destr_unit:_[0-9]+]] = move [[init_unit]]; | 
|  | // CHECK: [[destr_a:_[0-9]+]] = const "a"; | 
|  | // CHECK: [[destr_opt_isize:_[0-9]+]] = move [[init_opt_isize]]; | 
|  |  | 
|  | let Foo { a, b, c, d } = Foo { a: 5, b: (), c: "a", d: Some(-4) }; | 
|  | let _ = a; | 
|  | let _ = b; | 
|  | let _ = c; | 
|  | let _ = d; | 
|  | } | 
|  |  | 
|  | #[repr(C)] | 
|  | struct Escaping { | 
|  | a: u32, | 
|  | b: u32, | 
|  | c: u32, | 
|  | } | 
|  |  | 
|  | fn g() -> u32 { | 
|  | 3 | 
|  | } | 
|  |  | 
|  | fn f(a: *const u32) { | 
|  | println!("{}", unsafe { *a.add(2) }); | 
|  | } | 
|  |  | 
|  | // `f` uses the `&e.a` to access `e.c`. This is UB according to Miri today; however, | 
|  | // T-opsem has not finalized that decision and as such rustc should not rely on | 
|  | // it. If SROA were to rely on it, it would be (almost) correct to turn `e` into | 
|  | // three distinct locals - one for each field - and pass a reference to only one | 
|  | // of them to `f`. However, this would lead to a miscompilation because `b` and `c` | 
|  | // might no longer appear right after `a` in memory. | 
|  | pub fn escaping() { | 
|  | // CHECK-LABEL: fn escaping( | 
|  |  | 
|  | // CHECK: [[ptr:_[0-9]+]]: *const u32; | 
|  | // CHECK: [[ref:_[0-9]+]]: &u32; | 
|  | // CHECK: [[struct:_[0-9]+]]: Escaping; | 
|  | // CHECK: [[a:_[0-9]+]]: u32; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK: [[struct]] = Escaping { | 
|  | // CHECK: [[ref]] = &([[struct]].0 | 
|  | // CHECK: [[ptr]] = &raw const (*[[ref]]); | 
|  | f(&Escaping { a: 1, b: 2, c: g() }.a); | 
|  | } | 
|  |  | 
|  | /// Check that copies from an internal struct are destructured and reassigned to | 
|  | /// the original struct. | 
|  | fn copies(x: Foo) { | 
|  | // CHECK-LABEL: fn copies( | 
|  |  | 
|  | // CHECK: [[external:_[0-9]+]]: Foo) -> | 
|  | // CHECK: [[internal:_[0-9]+]]: Foo; | 
|  | // CHECK: [[byte:_[0-9]+]]: u8; | 
|  | // CHECK: [[unit:_[0-9]+]]: (); | 
|  | // CHECK: [[str:_[0-9]+]]: &str; | 
|  | // CHECK: [[opt_isize:_[0-9]+]]: std::option::Option<isize>; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK: [[byte]] = copy ([[external]].0 | 
|  | // CHECK: [[unit]] = copy ([[external]].1 | 
|  | // CHECK: [[str]] = copy ([[external]].2 | 
|  | // CHECK: [[opt_isize]] = copy ([[external]].3 | 
|  |  | 
|  | let y = x; | 
|  | let t = y.a; | 
|  | let u = y.c; | 
|  | let z = y; | 
|  | let a = z.b; | 
|  | } | 
|  |  | 
|  | /// Check that copies from an internal struct are destructured and reassigned to | 
|  | /// the original struct. | 
|  | fn ref_copies(x: &Foo) { | 
|  | // CHECK-LABEL: fn ref_copies( | 
|  |  | 
|  | // CHECK: [[external:_[0-9]+]]: &Foo) -> | 
|  | // CHECK: [[internal:_[0-9]+]]: Foo; | 
|  | // CHECK: [[byte:_[0-9]+]]: u8; | 
|  | // CHECK: [[unit:_[0-9]+]]: (); | 
|  | // CHECK: [[str:_[0-9]+]]: &str; | 
|  | // CHECK: [[opt_isize:_[0-9]+]]: std::option::Option<isize>; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK: [[byte]] = copy ((*[[external]]).0 | 
|  | // CHECK: [[unit]] = copy ((*[[external]]).1 | 
|  | // CHECK: [[str]] = copy ((*[[external]]).2 | 
|  | // CHECK: [[opt_isize]] = copy ((*[[external]]).3 | 
|  |  | 
|  | let y = *x; | 
|  | let t = y.a; | 
|  | let u = y.c; | 
|  | } | 
|  |  | 
|  | /// Check that deaggregated assignments from constants are placed after the constant's | 
|  | /// assignment. Also check that copying field accesses from the copy of the constant are | 
|  | /// reassigned to copy from the constant. | 
|  | fn constant() { | 
|  | // CHECK-LABEL: constant( | 
|  |  | 
|  | // CHECK: [[constant:_[0-9]+]]: (usize, u8); | 
|  | // CHECK: [[t:_[0-9]+]]: usize; | 
|  | // CHECK: [[u:_[0-9]+]]: u8; | 
|  |  | 
|  | // CHECK: bb0: { | 
|  | // CHECK-NOT: [[constant]] | 
|  | // CHECK: [[constant]] = const | 
|  | // CHECK: [[t]] = move ([[constant]].0: usize) | 
|  | // CHECK: [[u]] = move ([[constant]].1: u8) | 
|  | const U: (usize, u8) = (5, 9); | 
|  | let y = U; | 
|  | let t = y.0; | 
|  | let u = y.1; | 
|  | } | 
|  |  | 
|  | fn main() { | 
|  | // CHECK-LABEL: fn main( | 
|  | dropping(); | 
|  | enums(5); | 
|  | structs(5.); | 
|  | unions(5.); | 
|  | flat(); | 
|  | escaping(); | 
|  | copies(Foo { a: 5, b: (), c: "a", d: Some(-4) }); | 
|  | ref_copies(&Foo { a: 5, b: (), c: "a", d: Some(-4) }); | 
|  | constant(); | 
|  | } | 
|  |  | 
|  | // EMIT_MIR structs.dropping.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.enums.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.structs.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.unions.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.flat.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.escaping.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.copies.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.ref_copies.ScalarReplacementOfAggregates.diff | 
|  | // EMIT_MIR structs.constant.ScalarReplacementOfAggregates.diff |