Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't mark borrows of zero-sized arrays as indirectly mutable #64967

Merged
merged 3 commits into from
Oct 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions src/librustc_mir/dataflow/impls/indirect_mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,28 +97,44 @@ struct TransferFunction<'a, 'mir, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
}

impl<'tcx> TransferFunction<'_, '_, 'tcx> {
/// Returns `true` if this borrow would allow mutation of the `borrowed_place`.
fn borrow_allows_mutation(
&self,
kind: mir::BorrowKind,
borrowed_place: &mir::Place<'tcx>,
) -> bool {
let borrowed_ty = borrowed_place.ty(self.body, self.tcx).ty;

// Zero-sized types cannot be mutated, since there is nothing inside to mutate.
//
// FIXME: For now, we only exempt arrays of length zero. We need to carefully
// consider the effects before extending this to all ZSTs.
if let ty::Array(_, len) = borrowed_ty.kind {
if len.try_eval_usize(self.tcx, self.param_env) == Some(0) {
return false;
}
}

match kind {
mir::BorrowKind::Mut { .. } => true,

| mir::BorrowKind::Shared
| mir::BorrowKind::Shallow
| mir::BorrowKind::Unique
=> !borrowed_ty.is_freeze(self.tcx, self.param_env, DUMMY_SP),
}
}
}

impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
fn visit_rvalue(
&mut self,
rvalue: &mir::Rvalue<'tcx>,
location: Location,
) {
if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
let is_mut = match kind {
mir::BorrowKind::Mut { .. } => true,

| mir::BorrowKind::Shared
| mir::BorrowKind::Shallow
| mir::BorrowKind::Unique
=> {
!borrowed_place
.ty(self.body, self.tcx)
.ty
.is_freeze(self.tcx, self.param_env, DUMMY_SP)
}
};

if is_mut {
if self.borrow_allows_mutation(kind, borrowed_place) {
match borrowed_place.base {
mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect()
=> self.trans.gen(borrowed_local),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/check_consts/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ pub fn compute_indirectly_mutable_locals<'mir, 'tcx>(
item.tcx,
item.body,
item.def_id,
&[],
&item.tcx.get_attrs(item.def_id),
&dead_unwinds,
old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
|_, local| old_dataflow::DebugFormatted::new(&local),
Expand Down
31 changes: 31 additions & 0 deletions src/test/ui/consts/const-eval/generic-slice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Several variants of #64945.

// This struct is not important, we just use it to put `T` and `'a` in scope for our associated
// consts.
struct Generic<'a, T>(std::marker::PhantomData<&'a T>);

impl<'a, T: 'static> Generic<'a, T> {
const EMPTY_SLICE: &'a [T] = {
let x: &'a [T] = &[];
x
};

const EMPTY_SLICE_REF: &'a &'static [T] = {
let x: &'static [T] = &[];
&x
//~^ ERROR `x` does not live long enough
};
}

static mut INTERIOR_MUT_AND_DROP: &'static [std::cell::RefCell<Vec<i32>>] = {
let x: &[_] = &[];
x
};

static mut INTERIOR_MUT_AND_DROP_REF: &'static &'static [std::cell::RefCell<Vec<i32>>] = {
let x: &[_] = &[];
&x
//~^ ERROR `x` does not live long enough
};

fn main() {}
30 changes: 30 additions & 0 deletions src/test/ui/consts/const-eval/generic-slice.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error[E0597]: `x` does not live long enough
--> $DIR/generic-slice.rs:15:9
|
LL | impl<'a, T: 'static> Generic<'a, T> {
| -- lifetime `'a` defined here
...
LL | &x
| ^^
| |
| borrowed value does not live long enough
| using this value as a constant requires that `x` is borrowed for `'a`
LL |
LL | };
| - `x` dropped here while still borrowed

error[E0597]: `x` does not live long enough
--> $DIR/generic-slice.rs:27:5
|
LL | &x
| ^^
| |
| borrowed value does not live long enough
| using this value as a static requires that `x` is borrowed for `'static`
LL |
LL | };
| - `x` dropped here while still borrowed

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0597`.