diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 82b300dcb17d9..250ca798cd465 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1946,6 +1946,11 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { debug!("check_if_full_path_is_moved place: {:?}", place_span.0); let (prefix, mpi) = self.move_path_closest_to(place_span.0); + + if prefix == place_span.0 && !self.forbid_structural_initialization(mpi) { + return; + } + if maybe_uninits.contains(mpi) { self.report_use_of_moved_or_uninitialized( location, @@ -1987,6 +1992,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { if (from..to).contains(offset) { let uninit_child = self.move_data.find_in_move_path_or_its_descendants(child_mpi, |mpi| { + // FIXME(structural_init) you can't partially init an array element, right? maybe_uninits.contains(mpi) }); @@ -2059,9 +2065,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { debug!("check_if_path_or_subpath_is_moved place: {:?}", place_span.0); if let Some(mpi) = self.move_path_for_place(place_span.0) { - let uninit_mpi = self - .move_data - .find_in_move_path_or_its_descendants(mpi, |mpi| maybe_uninits.contains(mpi)); + let uninit_mpi = self.move_data.find_in_move_path_or_its_descendants(mpi, |mpi| { + maybe_uninits.contains(mpi) && self.forbid_structural_initialization(mpi) + }); if let Some(uninit_mpi) = uninit_mpi { self.report_use_of_moved_or_uninitialized( @@ -2075,6 +2081,46 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { } } + fn forbid_structural_initialization(&self, mpi: MovePathIndex) -> bool { + // FIXME: cache this + + let tcx = self.infcx.tcx; + + if !tcx.features().structural_init() { + return true; + } + + let path = &self.move_data.move_paths[mpi]; + + let field_count = match path.place.ty(self.body(), tcx).ty.kind() { + ty::Adt(adt, _) if adt.is_struct() && !adt.has_dtor(tcx) => { + let variant = adt.non_enum_variant(); + + if variant.field_list_has_applicable_non_exhaustive() { + return true; + } + + variant.fields.len() + } + ty::Tuple(tys) => tys.len(), + + _ => return true, + }; + + // A structurally initialized type is "uninit" but all of it's fields are init. + // This means all of it's fields must have MovePaths + // because fields that are never written to will not have MovePaths. + // Without this check, we may not detect that unwritten fields are uninit. + for field in (0..field_count).map(FieldIdx::from_usize) { + // FIXME WrapUnsafeBinder? + if self.move_data.rev_lookup.project(mpi, ProjectionElem::Field(field, ())).is_none() { + return true; + } + } + + false + } + /// Currently MoveData does not store entries for all places in /// the input MIR. For example it will currently filter out /// places that are Copy; thus we do not track places of shared @@ -2164,7 +2210,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // Once `let s; s.x = V; read(s.x);`, // is allowed, remove this match arm. - ty::Adt(..) | ty::Tuple(..) => { + ty::Adt(..) | ty::Tuple(..) if !tcx.features().structural_init() => { check_parent_of_field(self, location, place_base, span, state); } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index efd8bde71d76d..61f54f1ed316d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -241,6 +241,8 @@ declare_features! ( (unstable, sized_hierarchy, "1.89.0", None), /// Allows using the `#[stable]` and `#[unstable]` attributes. (internal, staged_api, "1.0.0", None), + /// FIXME: needs description and tracking issue. + (unstable, structural_init, "CURRENT_RUSTC_VERSION", None), /// Added for testing unstable lints; perma-unstable. (internal, test_unstable_lint, "1.60.0", None), /// Helps with formatting for `group_imports = "StdExternalCrate"`. diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 18985ba0da259..10d54116da9f2 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -324,7 +324,7 @@ impl<'tcx> MovePathLookup<'tcx> { }; for (_, elem) in self.un_derefer.iter_projections(place) { - if let Some(&subpath) = self.projections.get(&(result, elem.lift())) { + if let Some(subpath) = self.project(result, elem.lift()) { result = subpath; } else { return LookupResult::Parent(Some(result)); @@ -346,6 +346,10 @@ impl<'tcx> MovePathLookup<'tcx> { ) -> impl DoubleEndedIterator { self.locals.iter_enumerated().filter_map(|(l, &idx)| Some((l, idx?))) } + + pub fn project(&self, mpi: MovePathIndex, elem: ProjectionKind) -> Option { + self.projections.get(&(mpi, elem)).copied() + } } impl<'tcx> MoveData<'tcx> { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 09f01d8704e2a..bd862e7810468 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2099,6 +2099,7 @@ symbols! { struct_field_attributes, struct_inherit, struct_variant, + structural_init, structural_match, structural_peq, sub, diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index cac4dba2b49d7..54c49158c41ad 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2971,8 +2971,6 @@ ui/nll/issue-112604-closure-output-normalize.rs ui/nll/issue-16223.rs ui/nll/issue-21114-ebfull.rs ui/nll/issue-21114-kixunil.rs -ui/nll/issue-21232-partial-init-and-erroneous-use.rs -ui/nll/issue-21232-partial-init-and-use.rs ui/nll/issue-22323-temp-destruction.rs ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs ui/nll/issue-27282-move-match-input-into-guard.rs diff --git a/tests/ui/nll/issue-21232-partial-init-and-use.rs b/tests/ui/borrowck/structural_init.rs similarity index 76% rename from tests/ui/nll/issue-21232-partial-init-and-use.rs rename to tests/ui/borrowck/structural_init.rs index ad3eb248351d4..7ea75ca8313cb 100644 --- a/tests/ui/nll/issue-21232-partial-init-and-use.rs +++ b/tests/ui/borrowck/structural_init.rs @@ -1,16 +1,17 @@ +//@ revisions: stable feature +//@[feature] run-pass +// gate-test-structural_init +#![cfg_attr(feature, feature(structural_init))] + // This test enumerates various cases of interest for partial // [re]initialization of ADTs and tuples. // -// See rust-lang/rust#21232, rust-lang/rust#54986, and rust-lang/rust#54987. -// -// All of tests in this file are expected to change from being -// rejected, at least under NLL (by rust-lang/rust#54986) to being -// **accepted** when rust-lang/rust#54987 is implemented. -// (That's why there are assertions in the code.) +// All of the tests in this file should fail to compile normally +// and compile with `feature(structural_init)`. // -// See issue-21232-partial-init-and-erroneous-use.rs for cases of -// tests that are meant to continue failing to compile once -// rust-lang/rust#54987 is implemented. +// See structural_init_invalid.rs for cases of +// tests that are meant to continue failing to compile +// with `feature(structural_init)`. struct S { x: u32, @@ -37,11 +38,13 @@ fn borrow_t(t: &T) { assert_eq!(t.0, 10); assert_eq!(*t.1, 20); } fn move_t(t: T) { assert_eq!(t.0, 10); assert_eq!(*t.1, 20); } struct Q { + #[allow(dead_code)] v: u32, r: R, } struct R { + #[allow(dead_code)] w: u32, f: F, } @@ -49,6 +52,8 @@ struct R { impl Q { fn new(f: F) -> Self { Q { v: 0, r: R::new(f) } } } impl R { fn new(f: F) -> Self { R { w: 0, f } } } +struct Empty; + // Axes to cover: // * local/field: Is the structure in a local or a field // * fully/partial/void: Are we fully initializing it before using any part? @@ -94,65 +99,65 @@ macro_rules! use_part { fn test_0000_local_fully_init_and_use_struct() { let s: S; - s.x = 10; s.y = Box::new(20); //~ ERROR E0381 + s.x = 10; s.y = Box::new(20); //[stable]~ ERROR E0381 use_fully!(struct s); } fn test_0001_local_fully_init_and_use_tuple() { let t: T; - t.0 = 10; t.1 = Box::new(20); //~ ERROR E0381 + t.0 = 10; t.1 = Box::new(20); //[stable]~ ERROR E0381 use_fully!(tuple t); } fn test_0010_local_fully_reinit_and_use_struct() { let mut s: S = S::new(); drop(s); s.x = 10; s.y = Box::new(20); - //~^ ERROR assign to part of moved value: `s` [E0382] + //[stable]~^ ERROR assign to part of moved value: `s` [E0382] use_fully!(struct s); } fn test_0011_local_fully_reinit_and_use_tuple() { let mut t: T = (0, Box::new(0)); drop(t); t.0 = 10; t.1 = Box::new(20); - //~^ ERROR assign to part of moved value: `t` [E0382] + //[stable]~^ ERROR assign to part of moved value: `t` [E0382] use_fully!(tuple t); } fn test_0100_local_partial_init_and_use_struct() { let s: S; - s.x = 10; //~ ERROR E0381 + s.x = 10; //[stable]~ ERROR E0381 use_part!(struct s); } fn test_0101_local_partial_init_and_use_tuple() { let t: T; - t.0 = 10; //~ ERROR E0381 + t.0 = 10; //[stable]~ ERROR E0381 use_part!(tuple t); } fn test_0110_local_partial_reinit_and_use_struct() { let mut s: S = S::new(); drop(s); s.x = 10; - //~^ ERROR assign to part of moved value: `s` [E0382] + //[stable]~^ ERROR assign to part of moved value: `s` [E0382] use_part!(struct s); } fn test_0111_local_partial_reinit_and_use_tuple() { let mut t: T = (0, Box::new(0)); drop(t); t.0 = 10; - //~^ ERROR assign to part of moved value: `t` [E0382] + //[stable]~^ ERROR assign to part of moved value: `t` [E0382] use_part!(tuple t); } fn test_0200_local_void_init_and_use_struct() { let s: S; - s.x = 10; //~ ERROR E0381 + s.x = 10; //[stable]~ ERROR E0381 use_part!(struct s); } fn test_0201_local_void_init_and_use_tuple() { let t: Tvoid; - t.0 = 10; //~ ERROR E0381 + t.0 = 10; //[stable]~ ERROR E0381 use_part!(tuple t); } @@ -167,65 +172,65 @@ fn test_0201_local_void_init_and_use_tuple() { fn test_1000_field_fully_init_and_use_struct() { let q: Q>; - q.r.f.x = 10; q.r.f.y = Box::new(20); //~ ERROR E0381 + q.r.f.x = 10; q.r.f.y = Box::new(20); //[stable]~ ERROR E0381 use_fully!(struct q.r.f); } fn test_1001_field_fully_init_and_use_tuple() { let q: Q; - q.r.f.0 = 10; q.r.f.1 = Box::new(20); //~ ERROR E0381 + q.r.f.0 = 10; q.r.f.1 = Box::new(20); //[stable]~ ERROR E0381 use_fully!(tuple q.r.f); } fn test_1010_field_fully_reinit_and_use_struct() { let mut q: Q> = Q::new(S::new()); drop(q.r); q.r.f.x = 10; q.r.f.y = Box::new(20); - //~^ ERROR assign to part of moved value: `q.r` [E0382] + //[stable]~^ ERROR assign to part of moved value: `q.r` [E0382] use_fully!(struct q.r.f); } fn test_1011_field_fully_reinit_and_use_tuple() { let mut q: Q = Q::new((0, Box::new(0))); drop(q.r); q.r.f.0 = 10; q.r.f.1 = Box::new(20); - //~^ ERROR assign to part of moved value: `q.r` [E0382] + //[stable]~^ ERROR assign to part of moved value: `q.r` [E0382] use_fully!(tuple q.r.f); } fn test_1100_field_partial_init_and_use_struct() { let q: Q>; - q.r.f.x = 10; //~ ERROR E0381 + q.r.f.x = 10; //[stable]~ ERROR E0381 use_part!(struct q.r.f); } fn test_1101_field_partial_init_and_use_tuple() { let q: Q; - q.r.f.0 = 10; //~ ERROR E0381 + q.r.f.0 = 10; //[stable]~ ERROR E0381 use_part!(tuple q.r.f); } fn test_1110_field_partial_reinit_and_use_struct() { let mut q: Q> = Q::new(S::new()); drop(q.r); q.r.f.x = 10; - //~^ ERROR assign to part of moved value: `q.r` [E0382] + //[stable]~^ ERROR assign to part of moved value: `q.r` [E0382] use_part!(struct q.r.f); } fn test_1111_field_partial_reinit_and_use_tuple() { let mut q: Q = Q::new((0, Box::new(0))); drop(q.r); q.r.f.0 = 10; - //~^ ERROR assign to part of moved value: `q.r` [E0382] + //[stable]~^ ERROR assign to part of moved value: `q.r` [E0382] use_part!(tuple q.r.f); } fn test_1200_field_void_init_and_use_struct() { - let mut q: Q>; - q.r.f.x = 10; //~ ERROR E0381 + let q: Q>; + q.r.f.x = 10; //[stable]~ ERROR E0381 use_part!(struct q.r.f); } fn test_1201_field_void_init_and_use_tuple() { - let mut q: Q; - q.r.f.0 = 10; //~ ERROR E0381 + let q: Q; + q.r.f.0 = 10; //[stable]~ ERROR E0381 use_part!(tuple q.r.f); } @@ -242,7 +247,7 @@ fn issue_26996() { let mut c = (1, "".to_owned()); match c { c2 => { - c.0 = 2; //~ ERROR assign to part of moved value + c.0 = 2; //[stable]~ ERROR assign to part of moved value assert_eq!(c2.0, 1); } } @@ -252,7 +257,7 @@ fn issue_27021() { let mut c = (1, (1, "".to_owned())); match c { c2 => { - (c.1).0 = 2; //~ ERROR assign to part of moved value + (c.1).0 = 2; //[stable]~ ERROR assign to part of moved value assert_eq!((c2.1).0, 1); } } @@ -260,12 +265,25 @@ fn issue_27021() { let mut c = (1, (1, (1, "".to_owned()))); match c.1 { c2 => { - ((c.1).1).0 = 3; //~ ERROR assign to part of moved value + ((c.1).1).0 = 3; //[stable]~ ERROR assign to part of moved value assert_eq!((c2.1).0, 1); } } } +// Strange case discovered during implementation. +// FIXME: not decided if this should compile or not. +fn test_empty_struct() { + let e: Empty; + drop(e); //[stable]~ ERROR used binding `e` isn't initialized [E0381] +} + +#[expect(dropping_copy_types)] +fn test_empty_tuple() { + let t: (); + drop(t); //[stable]~ ERROR used binding `t` isn't initialized [E0381] +} + fn main() { test_0000_local_fully_init_and_use_struct(); test_0001_local_fully_init_and_use_tuple(); @@ -294,4 +312,7 @@ fn main() { issue_26996(); issue_27021(); + + test_empty_struct(); + test_empty_tuple(); } diff --git a/tests/ui/nll/issue-21232-partial-init-and-use.stderr b/tests/ui/borrowck/structural_init.stable.stderr similarity index 85% rename from tests/ui/nll/issue-21232-partial-init-and-use.stderr rename to tests/ui/borrowck/structural_init.stable.stderr index 496a298a36ce9..26607c0fd9a20 100644 --- a/tests/ui/nll/issue-21232-partial-init-and-use.stderr +++ b/tests/ui/borrowck/structural_init.stable.stderr @@ -1,5 +1,5 @@ error[E0381]: partially assigned binding `s` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:97:5 + --> $DIR/structural_init.rs:102:5 | LL | let s: S; | - binding declared here but left uninitialized @@ -9,7 +9,7 @@ LL | s.x = 10; s.y = Box::new(20); = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `t` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:103:5 + --> $DIR/structural_init.rs:108:5 | LL | let t: T; | - binding declared here but left uninitialized @@ -19,7 +19,7 @@ LL | t.0 = 10; t.1 = Box::new(20); = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0382]: assign to part of moved value: `s` - --> $DIR/issue-21232-partial-init-and-use.rs:109:5 + --> $DIR/structural_init.rs:114:5 | LL | let mut s: S = S::new(); drop(s); | ----- - value moved here @@ -29,7 +29,7 @@ LL | s.x = 10; s.y = Box::new(20); | ^^^^^^^^ value partially assigned here after move | note: if `S>` implemented `Clone`, you could clone the value - --> $DIR/issue-21232-partial-init-and-use.rs:15:1 + --> $DIR/structural_init.rs:16:1 | LL | struct S { | ^^^^^^^^^^^ consider implementing `Clone` for this type @@ -38,7 +38,7 @@ LL | let mut s: S = S::new(); drop(s); | - you could clone this value error[E0382]: assign to part of moved value: `t` - --> $DIR/issue-21232-partial-init-and-use.rs:116:5 + --> $DIR/structural_init.rs:121:5 | LL | let mut t: T = (0, Box::new(0)); drop(t); | ----- - value moved here @@ -53,7 +53,7 @@ LL | let mut t: T = (0, Box::new(0)); drop(t.clone()); | ++++++++ error[E0381]: partially assigned binding `s` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:123:5 + --> $DIR/structural_init.rs:128:5 | LL | let s: S; | - binding declared here but left uninitialized @@ -63,7 +63,7 @@ LL | s.x = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `t` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:129:5 + --> $DIR/structural_init.rs:134:5 | LL | let t: T; | - binding declared here but left uninitialized @@ -73,7 +73,7 @@ LL | t.0 = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0382]: assign to part of moved value: `s` - --> $DIR/issue-21232-partial-init-and-use.rs:135:5 + --> $DIR/structural_init.rs:140:5 | LL | let mut s: S = S::new(); drop(s); | ----- - value moved here @@ -83,7 +83,7 @@ LL | s.x = 10; | ^^^^^^^^ value partially assigned here after move | note: if `S>` implemented `Clone`, you could clone the value - --> $DIR/issue-21232-partial-init-and-use.rs:15:1 + --> $DIR/structural_init.rs:16:1 | LL | struct S { | ^^^^^^^^^^^ consider implementing `Clone` for this type @@ -92,7 +92,7 @@ LL | let mut s: S = S::new(); drop(s); | - you could clone this value error[E0382]: assign to part of moved value: `t` - --> $DIR/issue-21232-partial-init-and-use.rs:142:5 + --> $DIR/structural_init.rs:147:5 | LL | let mut t: T = (0, Box::new(0)); drop(t); | ----- - value moved here @@ -107,7 +107,7 @@ LL | let mut t: T = (0, Box::new(0)); drop(t.clone()); | ++++++++ error[E0381]: partially assigned binding `s` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:149:5 + --> $DIR/structural_init.rs:154:5 | LL | let s: S; | - binding declared here but left uninitialized @@ -117,7 +117,7 @@ LL | s.x = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `t` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:155:5 + --> $DIR/structural_init.rs:160:5 | LL | let t: Tvoid; | - binding declared here but left uninitialized @@ -127,7 +127,7 @@ LL | t.0 = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `q` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:170:5 + --> $DIR/structural_init.rs:175:5 | LL | let q: Q>; | - binding declared here but left uninitialized @@ -137,7 +137,7 @@ LL | q.r.f.x = 10; q.r.f.y = Box::new(20); = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `q` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:176:5 + --> $DIR/structural_init.rs:181:5 | LL | let q: Q; | - binding declared here but left uninitialized @@ -147,7 +147,7 @@ LL | q.r.f.0 = 10; q.r.f.1 = Box::new(20); = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0382]: assign to part of moved value: `q.r` - --> $DIR/issue-21232-partial-init-and-use.rs:182:5 + --> $DIR/structural_init.rs:187:5 | LL | let mut q: Q> = Q::new(S::new()); drop(q.r); | --- value moved here @@ -157,7 +157,7 @@ LL | q.r.f.x = 10; q.r.f.y = Box::new(20); = note: move occurs because `q.r` has type `R>>`, which does not implement the `Copy` trait error[E0382]: assign to part of moved value: `q.r` - --> $DIR/issue-21232-partial-init-and-use.rs:189:5 + --> $DIR/structural_init.rs:194:5 | LL | let mut q: Q = Q::new((0, Box::new(0))); drop(q.r); | --- value moved here @@ -167,7 +167,7 @@ LL | q.r.f.0 = 10; q.r.f.1 = Box::new(20); = note: move occurs because `q.r` has type `R<(u32, Box)>`, which does not implement the `Copy` trait error[E0381]: partially assigned binding `q` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:196:5 + --> $DIR/structural_init.rs:201:5 | LL | let q: Q>; | - binding declared here but left uninitialized @@ -177,7 +177,7 @@ LL | q.r.f.x = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `q` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:202:5 + --> $DIR/structural_init.rs:207:5 | LL | let q: Q; | - binding declared here but left uninitialized @@ -187,7 +187,7 @@ LL | q.r.f.0 = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0382]: assign to part of moved value: `q.r` - --> $DIR/issue-21232-partial-init-and-use.rs:208:5 + --> $DIR/structural_init.rs:213:5 | LL | let mut q: Q> = Q::new(S::new()); drop(q.r); | --- value moved here @@ -197,7 +197,7 @@ LL | q.r.f.x = 10; = note: move occurs because `q.r` has type `R>>`, which does not implement the `Copy` trait error[E0382]: assign to part of moved value: `q.r` - --> $DIR/issue-21232-partial-init-and-use.rs:215:5 + --> $DIR/structural_init.rs:220:5 | LL | let mut q: Q = Q::new((0, Box::new(0))); drop(q.r); | --- value moved here @@ -207,27 +207,27 @@ LL | q.r.f.0 = 10; = note: move occurs because `q.r` has type `R<(u32, Box)>`, which does not implement the `Copy` trait error[E0381]: partially assigned binding `q` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:222:5 + --> $DIR/structural_init.rs:227:5 | -LL | let mut q: Q>; - | ----- binding declared here but left uninitialized +LL | let q: Q>; + | - binding declared here but left uninitialized LL | q.r.f.x = 10; | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized | = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `q` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-use.rs:228:5 + --> $DIR/structural_init.rs:233:5 | -LL | let mut q: Q; - | ----- binding declared here but left uninitialized +LL | let q: Q; + | - binding declared here but left uninitialized LL | q.r.f.0 = 10; | ^^^^^^^^^^^^ `q.r.f` partially assigned here but it isn't fully initialized | = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0382]: assign to part of moved value: `c` - --> $DIR/issue-21232-partial-init-and-use.rs:245:13 + --> $DIR/structural_init.rs:250:13 | LL | let mut c = (1, "".to_owned()); | ----- move occurs because `c` has type `(i32, String)`, which does not implement the `Copy` trait @@ -243,7 +243,7 @@ LL | ref c2 => { | +++ error[E0382]: assign to part of moved value: `c` - --> $DIR/issue-21232-partial-init-and-use.rs:255:13 + --> $DIR/structural_init.rs:260:13 | LL | let mut c = (1, (1, "".to_owned())); | ----- move occurs because `c` has type `(i32, (i32, String))`, which does not implement the `Copy` trait @@ -259,7 +259,7 @@ LL | ref c2 => { | +++ error[E0382]: assign to part of moved value: `c.1` - --> $DIR/issue-21232-partial-init-and-use.rs:263:13 + --> $DIR/structural_init.rs:268:13 | LL | c2 => { | -- value moved here @@ -272,7 +272,33 @@ help: borrow this binding in the pattern to avoid moving the value LL | ref c2 => { | +++ -error: aborting due to 23 previous errors +error[E0381]: used binding `e` isn't initialized + --> $DIR/structural_init.rs:278:10 + | +LL | let e: Empty; + | - binding declared here but left uninitialized +LL | drop(e); + | ^ `e` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let e: Empty = /* value */; + | +++++++++++++ + +error[E0381]: used binding `t` isn't initialized + --> $DIR/structural_init.rs:284:10 + | +LL | let t: (); + | - binding declared here but left uninitialized +LL | drop(t); + | ^ `t` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let t: () = (); + | ++++ + +error: aborting due to 25 previous errors Some errors have detailed explanations: E0381, E0382. For more information about an error, try `rustc --explain E0381`. diff --git a/tests/ui/borrowck/structural_init_invalid.feature.stderr b/tests/ui/borrowck/structural_init_invalid.feature.stderr new file mode 100644 index 0000000000000..32dbafddd6f80 --- /dev/null +++ b/tests/ui/borrowck/structural_init_invalid.feature.stderr @@ -0,0 +1,77 @@ +error[E0381]: assigned binding `d` isn't fully initialized + --> $DIR/structural_init_invalid.rs:33:5 + | +LL | let d: D; + | - binding declared here but left uninitialized +LL | d.x = 10; + | ^^^^^^^^ `d` assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: assigned binding `d` isn't fully initialized + --> $DIR/structural_init_invalid.rs:38:5 + | +LL | let mut d: D; + | ----- binding declared here but left uninitialized +LL | d.x = 10; + | ^^^^^^^^ `d` assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign of moved value: `d` + --> $DIR/structural_init_invalid.rs:44:5 + | +LL | let mut d = D { x: 0, s: S{ y: 0, z: 0 } }; + | ----- move occurs because `d` has type `D`, which does not implement the `Copy` trait +LL | drop(d); + | - value moved here +LL | d.x = 10; + | ^^^^^^^^ value assigned here after move + +error[E0381]: assigned binding `d` isn't fully initialized + --> $DIR/structural_init_invalid.rs:50:5 + | +LL | let d: D; + | - binding declared here but left uninitialized +LL | d.s.y = 20; + | ^^^^^^^^^^ `d` assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0381]: assigned binding `d` isn't fully initialized + --> $DIR/structural_init_invalid.rs:55:5 + | +LL | let mut d: D; + | ----- binding declared here but left uninitialized +LL | d.s.y = 20; + | ^^^^^^^^^^ `d` assigned here but it isn't fully initialized + | + = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` + +error[E0382]: assign of moved value: `d` + --> $DIR/structural_init_invalid.rs:61:5 + | +LL | let mut d = D { x: 0, s: S{ y: 0, z: 0} }; + | ----- move occurs because `d` has type `D`, which does not implement the `Copy` trait +LL | drop(d); + | - value moved here +LL | d.s.y = 20; + | ^^^^^^^^^^ value assigned here after move + +error[E0381]: used binding `e` isn't initialized + --> $DIR/structural_init_invalid.rs:69:10 + | +LL | let e: EmptyWithDrop; + | - binding declared here but left uninitialized +LL | drop(e); + | ^ `e` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let e: EmptyWithDrop = /* value */; + | +++++++++++++ + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0381, E0382. +For more information about an error, try `rustc --explain E0381`. diff --git a/tests/ui/nll/issue-21232-partial-init-and-erroneous-use.rs b/tests/ui/borrowck/structural_init_invalid.rs similarity index 62% rename from tests/ui/nll/issue-21232-partial-init-and-erroneous-use.rs rename to tests/ui/borrowck/structural_init_invalid.rs index 46a156d2af9ee..1dfb124aeefbb 100644 --- a/tests/ui/nll/issue-21232-partial-init-and-erroneous-use.rs +++ b/tests/ui/borrowck/structural_init_invalid.rs @@ -1,12 +1,12 @@ +//@ revisions: stable feature +#![cfg_attr(feature, feature(structural_init))] + // This test enumerates various cases of interest where an ADT or tuple is // partially initialized and then used in some way that is wrong *even* -// after rust-lang/rust#54987 is implemented. -// -// See rust-lang/rust#21232, rust-lang/rust#54986, and rust-lang/rust#54987. +// with `feature(structural_init)`. The error output in both cases should be identical. // -// See issue-21232-partial-init-and-use.rs for cases of tests that are -// meant to compile and run successfully once rust-lang/rust#54987 is -// implemented. +// See structural_init.rs for cases of tests that are +// meant to compile and run successfully with `structural_init`. struct D { x: u32, @@ -18,11 +18,16 @@ struct S { z: u32, } +struct EmptyWithDrop; impl Drop for D { fn drop(&mut self) { } } +impl Drop for EmptyWithDrop { + fn drop(&mut self) { } +} + fn cannot_partially_init_adt_with_drop() { let d: D; d.x = 10; //~ ERROR E0381 @@ -54,7 +59,14 @@ fn cannot_partially_reinit_inner_adt_via_outer_with_drop() { let mut d = D { x: 0, s: S{ y: 0, z: 0} }; drop(d); d.s.y = 20; - //~^ ERROR assign to part of moved value: `d` [E0382] + //[stable]~^ ERROR assign to part of moved value: `d` [E0382] + //[feature]~^^ ERROR assign of moved value: `d` [E0382] + // FIXME: nonsense diagnostic +} + +fn empty_struct_with_drop() { + let e: EmptyWithDrop; + drop(e); //~ ERROR E0381 } fn main() { } diff --git a/tests/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr b/tests/ui/borrowck/structural_init_invalid.stable.stderr similarity index 77% rename from tests/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr rename to tests/ui/borrowck/structural_init_invalid.stable.stderr index 63f230be7d4b3..34e2c382ab2b5 100644 --- a/tests/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr +++ b/tests/ui/borrowck/structural_init_invalid.stable.stderr @@ -1,5 +1,5 @@ error[E0381]: assigned binding `d` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:28:5 + --> $DIR/structural_init_invalid.rs:33:5 | LL | let d: D; | - binding declared here but left uninitialized @@ -9,7 +9,7 @@ LL | d.x = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: assigned binding `d` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:33:5 + --> $DIR/structural_init_invalid.rs:38:5 | LL | let mut d: D; | ----- binding declared here but left uninitialized @@ -19,7 +19,7 @@ LL | d.x = 10; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0382]: assign of moved value: `d` - --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:39:5 + --> $DIR/structural_init_invalid.rs:44:5 | LL | let mut d = D { x: 0, s: S{ y: 0, z: 0 } }; | ----- move occurs because `d` has type `D`, which does not implement the `Copy` trait @@ -29,7 +29,7 @@ LL | d.x = 10; | ^^^^^^^^ value assigned here after move error[E0381]: partially assigned binding `d` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:45:5 + --> $DIR/structural_init_invalid.rs:50:5 | LL | let d: D; | - binding declared here but left uninitialized @@ -39,7 +39,7 @@ LL | d.s.y = 20; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0381]: partially assigned binding `d` isn't fully initialized - --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:50:5 + --> $DIR/structural_init_invalid.rs:55:5 | LL | let mut d: D; | ----- binding declared here but left uninitialized @@ -49,7 +49,7 @@ LL | d.s.y = 20; = help: partial initialization isn't supported, fully initialize the binding with a default value and mutate it, or use `std::mem::MaybeUninit` error[E0382]: assign to part of moved value: `d` - --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:56:5 + --> $DIR/structural_init_invalid.rs:61:5 | LL | let mut d = D { x: 0, s: S{ y: 0, z: 0} }; | ----- move occurs because `d` has type `D`, which does not implement the `Copy` trait @@ -58,7 +58,20 @@ LL | drop(d); LL | d.s.y = 20; | ^^^^^^^^^^ value partially assigned here after move -error: aborting due to 6 previous errors +error[E0381]: used binding `e` isn't initialized + --> $DIR/structural_init_invalid.rs:69:10 + | +LL | let e: EmptyWithDrop; + | - binding declared here but left uninitialized +LL | drop(e); + | ^ `e` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let e: EmptyWithDrop = /* value */; + | +++++++++++++ + +error: aborting due to 7 previous errors Some errors have detailed explanations: E0381, E0382. For more information about an error, try `rustc --explain E0381`.