From cd9f5ff2a1c50a5af94ad1dd1c976f631b9f19a6 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 13 Feb 2020 20:29:03 +0000 Subject: [PATCH] Check `Copy` lifetimes bounds when copying from a projection --- .../borrow_check/type_check/mod.rs | 52 +++++++++---------- ...not-ignore-lifetime-bounds-in-copy-proj.rs | 12 +++++ ...ignore-lifetime-bounds-in-copy-proj.stderr | 14 +++++ 3 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs create mode 100644 src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index 5dab064c7b7fc..232e7e6b83d2b 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -466,33 +466,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { let mut place_ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty); - if place.projection.is_empty() { - if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { - let tcx = self.tcx(); - let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().copy_trait().unwrap(), - substs: tcx.mk_substs_trait(place_ty.ty, &[]), - }; - - // To have a `Copy` operand, the type `T` of the - // value must be `Copy`. Note that we prove that `T: Copy`, - // rather than using the `is_copy_modulo_regions` - // test. This is important because - // `is_copy_modulo_regions` ignores the resulting region - // obligations and assumes they pass. This can result in - // bounds from `Copy` impls being unsoundly ignored (e.g., - // #29149). Note that we decide to use `Copy` before knowing - // whether the bounds fully apply: in effect, the rule is - // that if a value of some type could implement `Copy`, then - // it must. - self.cx.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::CopyBound, - ); - } - } - for elem in place.projection.iter() { if place_ty.variant_index.is_none() { if place_ty.ty.references_error() { @@ -503,6 +476,31 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { place_ty = self.sanitize_projection(place_ty, elem, place, location) } + if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { + let tcx = self.tcx(); + let trait_ref = ty::TraitRef { + def_id: tcx.lang_items().copy_trait().unwrap(), + substs: tcx.mk_substs_trait(place_ty.ty, &[]), + }; + + // To have a `Copy` operand, the type `T` of the + // value must be `Copy`. Note that we prove that `T: Copy`, + // rather than using the `is_copy_modulo_regions` + // test. This is important because + // `is_copy_modulo_regions` ignores the resulting region + // obligations and assumes they pass. This can result in + // bounds from `Copy` impls being unsoundly ignored (e.g., + // #29149). Note that we decide to use `Copy` before knowing + // whether the bounds fully apply: in effect, the rule is + // that if a value of some type could implement `Copy`, then + // it must. + self.cx.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::CopyBound, + ); + } + place_ty } diff --git a/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs new file mode 100644 index 0000000000000..96c8719468f27 --- /dev/null +++ b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs @@ -0,0 +1,12 @@ +// Test that the 'static bound from the Copy impl is respected. Regression test for #29149. + +#[derive(Clone)] +struct Foo<'a>(&'a u32); +impl Copy for Foo<'static> {} + +fn main() { + let s = 2; + let a = (Foo(&s),); //~ ERROR `s` does not live long enough [E0597] + drop(a.0); + drop(a.0); +} diff --git a/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr new file mode 100644 index 0000000000000..65be3b37e0e3b --- /dev/null +++ b/src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr @@ -0,0 +1,14 @@ +error[E0597]: `s` does not live long enough + --> $DIR/do-not-ignore-lifetime-bounds-in-copy-proj.rs:9:18 + | +LL | let a = (Foo(&s),); + | ^^ borrowed value does not live long enough +LL | drop(a.0); + | --- copying this value requires that `s` is borrowed for `'static` +LL | drop(a.0); +LL | } + | - `s` dropped here while still borrowed + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`.