Skip to content

Commit

Permalink
Check Copy lifetimes bounds when copying from a projection
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed Feb 13, 2020
1 parent d538b80 commit cd9f5ff
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 27 deletions.
52 changes: 25 additions & 27 deletions src/librustc_mir/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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
}

Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.rs
Original file line number Diff line number Diff line change
@@ -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);
}
14 changes: 14 additions & 0 deletions src/test/ui/nll/do-not-ignore-lifetime-bounds-in-copy-proj.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit cd9f5ff

Please sign in to comment.