Skip to content

Commit

Permalink
Rollup merge of #67467 - matthewjasper:test-slice-patterns, r=oli-obk
Browse files Browse the repository at this point in the history
Test slice patterns more

Adds tests for const evaluation and some more borrow checking tests.

Fixes some bugs in const eval for subslice patterns.
closes #66934

r? @oli-obk
cc @Centril
  • Loading branch information
Centril authored Dec 21, 2019
2 parents f43ced3 + 8c3c446 commit 0a2813c
Show file tree
Hide file tree
Showing 11 changed files with 703 additions and 4 deletions.
18 changes: 16 additions & 2 deletions src/librustc_mir/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,13 +444,27 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Field(field, _) => self.operand_field(base, field.index() as u64)?,
Downcast(_, variant) => self.operand_downcast(base, variant)?,
Deref => self.deref_operand(base)?.into(),
Subslice { .. } | ConstantIndex { .. } | Index(_) => if base.layout.is_zst() {
ConstantIndex { .. } | Index(_) if base.layout.is_zst() => {
OpTy {
op: Operand::Immediate(Scalar::zst().into()),
// the actual index doesn't matter, so we just pick a convenient one like 0
layout: base.layout.field(self, 0)?,
}
} else {
}
Subslice { from, to, from_end } if base.layout.is_zst() => {
let elem_ty = if let ty::Array(elem_ty, _) = base.layout.ty.kind {
elem_ty
} else {
bug!("slices shouldn't be zero-sized");
};
assert!(!from_end, "arrays shouldn't be subsliced from the end");

OpTy {
op: Operand::Immediate(Scalar::zst().into()),
layout: self.layout_of(self.tcx.mk_array(elem_ty, (to - from) as u64))?,
}
}
Subslice { .. } | ConstantIndex { .. } | Index(_) => {
// The rest should only occur as mplace, we do not use Immediates for types
// allowing such operations. This matches place_projection forcing an allocation.
let mplace = base.assert_mem_place();
Expand Down
11 changes: 9 additions & 2 deletions src/librustc_mir/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,10 @@ where
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
let len = base.len(self)?; // also asserts that we have a type where this makes sense
let actual_to = if from_end {
assert!(from <= len - to);
if from + to > len {
// This can only be reached in ConstProp and non-rustc-MIR.
throw_ub!(BoundsCheckFailed { len: len as u64, index: from as u64 + to as u64 });
}
len - to
} else {
to
Expand Down Expand Up @@ -523,7 +526,11 @@ where
from_end,
} => {
let n = base.len(self)?;
assert!(n >= min_length as u64);
if n < min_length as u64 {
// This can only be reached in ConstProp and non-rustc-MIR.
throw_ub!(BoundsCheckFailed { len: min_length as u64, index: n as u64 });
}
assert!(offset < min_length);

let index = if from_end {
n - u64::from(offset)
Expand Down
97 changes: 97 additions & 0 deletions src/test/ui/array-slice-vec/subslice-patterns-const-eval-match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Test that slice subslice patterns are correctly handled in const evaluation.

// run-pass

#![feature(slice_patterns, const_fn, const_if_match)]
#[derive(PartialEq, Debug, Clone)]
struct N(u8);

#[derive(PartialEq, Debug, Clone)]
struct Z;

macro_rules! n {
($($e:expr),* $(,)?) => {
[$(N($e)),*]
}
}

// This macro has an unused variable so that it can be repeated base on the
// number of times a repeated variable (`$e` in `z`) occurs.
macro_rules! zed {
($e:expr) => { Z }
}

macro_rules! z {
($($e:expr),* $(,)?) => {
[$(zed!($e)),*]
}
}

// Compare constant evaluation and runtime evaluation of a given expression.
macro_rules! compare_evaluation_inner {
($e:expr, $t:ty $(,)?) => {{
const CONST_EVAL: $t = $e;
const fn const_eval() -> $t { $e }
static CONST_EVAL2: $t = const_eval();
let runtime_eval = $e;
assert_eq!(CONST_EVAL, runtime_eval);
assert_eq!(CONST_EVAL2, runtime_eval);
}}
}

// Compare the result of matching `$e` against `$p` using both `if let` and
// `match`.
macro_rules! compare_evaluation {
($p:pat, $e:expr, $matches:expr, $t:ty $(,)?) => {{
compare_evaluation_inner!(if let $p = $e as &[_] { $matches } else { None }, $t);
compare_evaluation_inner!(match $e as &[_] { $p => $matches, _ => None }, $t);
}}
}

// Repeat `$test`, substituting the given macro variables with the given
// identifiers.
//
// For example:
//
// repeat! {
// ($name); X; Y:
// struct $name;
// }
//
// Expands to:
//
// struct X; struct Y;
//
// This is used to repeat the tests using both the `N` and `Z`
// types.
macro_rules! repeat {
(($($dollar:tt $placeholder:ident)*); $($($values:ident),+);*: $($test:tt)*) => {
macro_rules! single {
($($dollar $placeholder:ident),*) => { $($test)* }
}
$(single!($($values),+);)*
}
}

fn main() {
repeat! {
($arr $Ty); n, N; z, Z:
compare_evaluation!([_, x @ .., _], &$arr!(1, 2, 3, 4), Some(x), Option<&'static [$Ty]>);
compare_evaluation!([x, .., _], &$arr!(1, 2, 3, 4), Some(x), Option<&'static $Ty>);
compare_evaluation!([_, .., x], &$arr!(1, 2, 3, 4), Some(x), Option<&'static $Ty>);

compare_evaluation!([_, x @ .., _], &$arr!(1, 2), Some(x), Option<&'static [$Ty]>);
compare_evaluation!([x, .., _], &$arr!(1, 2), Some(x), Option<&'static $Ty>);
compare_evaluation!([_, .., x], &$arr!(1, 2), Some(x), Option<&'static $Ty>);

compare_evaluation!([_, x @ .., _], &$arr!(1), Some(x), Option<&'static [$Ty]>);
compare_evaluation!([x, .., _], &$arr!(1), Some(x), Option<&'static $Ty>);
compare_evaluation!([_, .., x], &$arr!(1), Some(x), Option<&'static $Ty>);
}

compare_evaluation!([N(x), .., _], &n!(1, 2, 3, 4), Some(x), Option<&'static u8>);
compare_evaluation!([_, .., N(x)], &n!(1, 2, 3, 4), Some(x), Option<&'static u8>);

compare_evaluation!([N(x), .., _], &n!(1, 2), Some(x), Option<&'static u8>);
compare_evaluation!([_, .., N(x)], &n!(1, 2), Some(x), Option<&'static u8>);
}
97 changes: 97 additions & 0 deletions src/test/ui/array-slice-vec/subslice-patterns-const-eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Test that array subslice patterns are correctly handled in const evaluation.

// run-pass

#![feature(slice_patterns)]

#[derive(PartialEq, Debug, Clone)]
struct N(u8);

#[derive(PartialEq, Debug, Clone)]
struct Z;

macro_rules! n {
($($e:expr),* $(,)?) => {
[$(N($e)),*]
}
}

// This macro has an unused variable so that it can be repeated base on the
// number of times a repeated variable (`$e` in `z`) occurs.
macro_rules! zed {
($e:expr) => { Z }
}

macro_rules! z {
($($e:expr),* $(,)?) => {
[$(zed!($e)),*]
}
}

// Compare constant evaluation and runtime evaluation of a given expression.
macro_rules! compare_evaluation {
($e:expr, $t:ty $(,)?) => {{
const CONST_EVAL: $t = $e;
const fn const_eval() -> $t { $e }
static CONST_EVAL2: $t = const_eval();
let runtime_eval = $e;
assert_eq!(CONST_EVAL, runtime_eval);
assert_eq!(CONST_EVAL2, runtime_eval);
}}
}

// Repeat `$test`, substituting the given macro variables with the given
// identifiers.
//
// For example:
//
// repeat! {
// ($name); X; Y:
// struct $name;
// }
//
// Expands to:
//
// struct X; struct Y;
//
// This is used to repeat the tests using both the `N` and `Z`
// types.
macro_rules! repeat {
(($($dollar:tt $placeholder:ident)*); $($($values:ident),+);*: $($test:tt)*) => {
macro_rules! single {
($($dollar $placeholder:ident),*) => { $($test)* }
}
$(single!($($values),+);)*
}
}

fn main() {
repeat! {
($arr $Ty); n, N; z, Z:
compare_evaluation!({ let [_, x @ .., _] = $arr!(1, 2, 3, 4); x }, [$Ty; 2]);
compare_evaluation!({ let [_, ref x @ .., _] = $arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);
compare_evaluation!({ let [_, x @ .., _] = &$arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);

compare_evaluation!({ let [_, _, x @ .., _, _] = $arr!(1, 2, 3, 4); x }, [$Ty; 0]);
compare_evaluation!(
{ let [_, _, ref x @ .., _, _] = $arr!(1, 2, 3, 4); x },
&'static [$Ty; 0],
);
compare_evaluation!(
{ let [_, _, x @ .., _, _] = &$arr!(1, 2, 3, 4); x },
&'static [$Ty; 0],
);

compare_evaluation!({ let [_, .., x] = $arr!(1, 2, 3, 4); x }, $Ty);
compare_evaluation!({ let [_, .., ref x] = $arr!(1, 2, 3, 4); x }, &'static $Ty);
compare_evaluation!({ let [_, _y @ .., x] = &$arr!(1, 2, 3, 4); x }, &'static $Ty);
}

compare_evaluation!({ let [_, .., N(x)] = n!(1, 2, 3, 4); x }, u8);
compare_evaluation!({ let [_, .., N(ref x)] = n!(1, 2, 3, 4); x }, &'static u8);
compare_evaluation!({ let [_, .., N(x)] = &n!(1, 2, 3, 4); x }, &'static u8);

compare_evaluation!({ let [N(x), .., _] = n!(1, 2, 3, 4); x }, u8);
compare_evaluation!({ let [N(ref x), .., _] = n!(1, 2, 3, 4); x }, &'static u8);
compare_evaluation!({ let [N(x), .., _] = &n!(1, 2, 3, 4); x }, &'static u8);
}
118 changes: 118 additions & 0 deletions src/test/ui/borrowck/borrowck-closures-slice-patterns-ok.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Check that closure captures for slice patterns are inferred correctly

#![feature(slice_patterns)]
#![allow(unused_variables)]

// run-pass

fn arr_by_ref(x: [String; 3]) {
let r = &x;
let f = || {
let [ref y, ref z @ ..] = x;
};
f();
f();
// Ensure `x` was borrowed
drop(r);
// Ensure that `x` wasn't moved from.
drop(x);
}

fn arr_by_mut(mut x: [String; 3]) {
let mut f = || {
let [ref mut y, ref mut z @ ..] = x;
};
f();
f();
drop(x);
}

fn arr_by_move(x: [String; 3]) {
let f = || {
let [y, z @ ..] = x;
};
f();
}

fn arr_ref_by_ref(x: &[String; 3]) {
let r = &x;
let f = || {
let [ref y, ref z @ ..] = *x;
};
let g = || {
let [y, z @ ..] = x;
};
f();
g();
f();
g();
drop(r);
drop(x);
}

fn arr_ref_by_mut(x: &mut [String; 3]) {
let mut f = || {
let [ref mut y, ref mut z @ ..] = *x;
};
f();
f();
let mut g = || {
let [y, z @ ..] = x;
// Ensure binding mode was chosen correctly:
std::mem::swap(y, &mut z[0]);
};
g();
g();
drop(x);
}

fn arr_box_by_move(x: Box<[String; 3]>) {
let f = || {
let [y, z @ ..] = *x;
};
f();
}

fn slice_by_ref(x: &[String]) {
let r = &x;
let f = || {
if let [ref y, ref z @ ..] = *x {}
};
let g = || {
if let [y, z @ ..] = x {}
};
f();
g();
f();
g();
drop(r);
drop(x);
}

fn slice_by_mut(x: &mut [String]) {
let mut f = || {
if let [ref mut y, ref mut z @ ..] = *x {}
};
f();
f();
let mut g = || {
if let [y, z @ ..] = x {
// Ensure binding mode was chosen correctly:
std::mem::swap(y, &mut z[0]);
}
};
g();
g();
drop(x);
}

fn main() {
arr_by_ref(Default::default());
arr_by_mut(Default::default());
arr_by_move(Default::default());
arr_ref_by_ref(&Default::default());
arr_ref_by_mut(&mut Default::default());
arr_box_by_move(Default::default());
slice_by_ref(&<[_; 3]>::default());
slice_by_mut(&mut <[_; 3]>::default());
}
Loading

0 comments on commit 0a2813c

Please sign in to comment.