-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Don't depend on Allocation
sizes for pattern length
#56540
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
Changes from 4 commits
a5a7fcb
8f2ce3d
8a9414a
7d1b19c
46c1da6
25a4f76
5df6af4
1c2a29e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -178,11 +178,11 @@ use super::{PatternFoldable, PatternFolder, compare_const_vals}; | |
|
||
use rustc::hir::def_id::DefId; | ||
use rustc::hir::RangeEnd; | ||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; | ||
use rustc::ty::layout::{Integer, IntegerExt, VariantIdx}; | ||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Const}; | ||
use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size}; | ||
|
||
use rustc::mir::Field; | ||
use rustc::mir::interpret::ConstValue; | ||
use rustc::mir::interpret::{ConstValue, Pointer, Scalar}; | ||
use rustc::util::common::ErrorReported; | ||
|
||
use syntax::attr::{SignedInt, UnsignedInt}; | ||
|
@@ -200,22 +200,72 @@ use std::u128; | |
pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pattern<'tcx>) | ||
-> &'a Pattern<'tcx> | ||
{ | ||
cx.pattern_arena.alloc(LiteralExpander.fold_pattern(&pat)) | ||
cx.pattern_arena.alloc(LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat)) | ||
} | ||
|
||
struct LiteralExpander; | ||
impl<'tcx> PatternFolder<'tcx> for LiteralExpander { | ||
struct LiteralExpander<'a, 'tcx> { | ||
tcx: TyCtxt<'a, 'tcx, 'tcx> | ||
} | ||
|
||
impl<'a, 'tcx> LiteralExpander<'a, 'tcx> { | ||
/// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice | ||
/// | ||
/// `crty` and `rty` can differ because you can use array constants in the presence of slice | ||
/// patterns. So the pattern may end up being a slice, but the constant is an array. We convert | ||
/// the array to a slice in that case | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: end comments with full stops (here and elsewhere). |
||
fn fold_const_value_deref( | ||
&mut self, | ||
val: ConstValue<'tcx>, | ||
// the pattern's pointee type | ||
rty: Ty<'tcx>, | ||
// the constant's pointee type | ||
crty: Ty<'tcx>, | ||
) -> ConstValue<'tcx> { | ||
match (val, &crty.sty, &rty.sty) { | ||
// the easy case, deref a reference | ||
(ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => ConstValue::ByRef( | ||
p.alloc_id, | ||
self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id), | ||
p.offset, | ||
), | ||
// unsize array to slice if pattern is array but match value or other patterns are slice | ||
(ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { | ||
assert_eq!(t, u); | ||
ConstValue::ScalarPair( | ||
Scalar::Ptr(p), | ||
n.val.try_to_scalar().unwrap(), | ||
) | ||
}, | ||
// fat pointers stay the same | ||
(ConstValue::ScalarPair(..), _, _) => val, | ||
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens in this case in the existing code? A There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently this is an ICE: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=76fe5897409eefe240b587fb5ba59f6e It will stay an ICE, but this PR is prepping the road for making it saner, because it's an explicit choice to ICE instead of just a random assert triggering. |
||
_ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), | ||
} | ||
} | ||
} | ||
|
||
impl<'a, 'tcx> PatternFolder<'tcx> for LiteralExpander<'a, 'tcx> { | ||
fn fold_pattern(&mut self, pat: &Pattern<'tcx>) -> Pattern<'tcx> { | ||
match (&pat.ty.sty, &*pat.kind) { | ||
(&ty::Ref(_, rty, _), &PatternKind::Constant { ref value }) => { | ||
( | ||
&ty::Ref(_, rty, _), | ||
&PatternKind::Constant { value: Const { | ||
val, | ||
ty: ty::TyS { sty: ty::Ref(_, crty, _), .. }, | ||
} }, | ||
) => { | ||
Pattern { | ||
ty: pat.ty, | ||
span: pat.span, | ||
kind: box PatternKind::Deref { | ||
subpattern: Pattern { | ||
ty: rty, | ||
span: pat.span, | ||
kind: box PatternKind::Constant { value: value.clone() }, | ||
kind: box PatternKind::Constant { value: Const::from_const_value( | ||
self.tcx, | ||
self.fold_const_value_deref(*val, rty, crty), | ||
rty, | ||
) }, | ||
} | ||
} | ||
} | ||
|
@@ -732,15 +782,17 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( | |
for row in patterns { | ||
match *row.kind { | ||
PatternKind::Constant { value } => { | ||
if let Some(ptr) = value.to_ptr() { | ||
let is_array_ptr = value.ty | ||
.builtin_deref(true) | ||
.and_then(|t| t.ty.builtin_index()) | ||
.map_or(false, |t| t == cx.tcx.types.u8); | ||
if is_array_ptr { | ||
let alloc = cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id); | ||
max_fixed_len = cmp::max(max_fixed_len, alloc.bytes.len() as u64); | ||
} | ||
// extract the length of an array/slice from a constant | ||
match (value.val, &value.ty.sty) { | ||
(_, ty::Array(_, n)) => max_fixed_len = cmp::max( | ||
max_fixed_len, | ||
n.unwrap_usize(cx.tcx), | ||
), | ||
(ConstValue::ScalarPair(_, n), ty::Slice(_)) => max_fixed_len = cmp::max( | ||
max_fixed_len, | ||
n.to_usize(&cx.tcx).unwrap(), | ||
), | ||
_ => {}, | ||
} | ||
} | ||
PatternKind::Slice { ref prefix, slice: None, ref suffix } => { | ||
|
@@ -1348,28 +1400,56 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, | |
} | ||
} | ||
|
||
fn slice_pat_covered_by_constructor<'tcx>( | ||
// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, | ||
// meaning all other types will compare unequal and thus equal patterns often do not cause the | ||
// second pattern to lint about unreachable match arms. | ||
fn slice_pat_covered_by_const<'tcx>( | ||
tcx: TyCtxt<'_, 'tcx, '_>, | ||
_span: Span, | ||
ctor: &Constructor, | ||
const_val: &ty::Const<'tcx>, | ||
prefix: &[Pattern<'tcx>], | ||
slice: &Option<Pattern<'tcx>>, | ||
suffix: &[Pattern<'tcx>] | ||
) -> Result<bool, ErrorReported> { | ||
let data: &[u8] = match *ctor { | ||
ConstantValue(const_val) => { | ||
let val = match const_val.val { | ||
ConstValue::Unevaluated(..) | | ||
ConstValue::ByRef(..) => bug!("unexpected ConstValue: {:?}", const_val), | ||
ConstValue::Scalar(val) | ConstValue::ScalarPair(val, _) => val, | ||
}; | ||
if let Ok(ptr) = val.to_ptr() { | ||
tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id).bytes.as_ref() | ||
} else { | ||
bug!("unexpected non-ptr ConstantValue") | ||
let data: &[u8] = match (const_val.val, &const_val.ty.sty) { | ||
(ConstValue::ByRef(id, alloc, offset), ty::Array(t, n)) => { | ||
if *t != tcx.types.u8 { | ||
// FIXME(oli-obk): can't mix const patterns with slice patterns and get | ||
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// any sort of exhaustiveness/unreachable check yet | ||
return Ok(false); | ||
} | ||
} | ||
_ => bug!() | ||
let ptr = Pointer::new(id, offset); | ||
let n = n.assert_usize(tcx).unwrap(); | ||
alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() | ||
}, | ||
// a slice fat pointer to a zero length slice | ||
(ConstValue::ScalarPair(Scalar::Bits { .. }, n), ty::Slice(t)) => { | ||
if *t != tcx.types.u8 { | ||
// FIXME(oli-obk): can't mix const patterns with slice patterns and get | ||
// any sort of exhaustiveness/unreachable check yet | ||
return Ok(false); | ||
} | ||
assert_eq!(n.to_usize(&tcx).unwrap(), 0); | ||
&[] | ||
}, | ||
// | ||
(ConstValue::ScalarPair(Scalar::Ptr(ptr), n), ty::Slice(t)) => { | ||
if *t != tcx.types.u8 { | ||
// FIXME(oli-obk): can't mix const patterns with slice patterns and get | ||
// any sort of exhaustiveness/unreachable check yet | ||
return Ok(false); | ||
} | ||
let n = n.to_usize(&tcx).unwrap(); | ||
tcx.alloc_map | ||
.lock() | ||
.unwrap_memory(ptr.alloc_id) | ||
.get_bytes(&tcx, ptr, Size::from_bytes(n)) | ||
.unwrap() | ||
}, | ||
_ => bug!( | ||
"slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", | ||
const_val, prefix, slice, suffix, | ||
), | ||
}; | ||
|
||
let pat_len = prefix.len() + suffix.len(); | ||
|
@@ -1675,22 +1755,23 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( | |
// necessarily point to memory, they are usually just integers. The only time | ||
// they should be pointing to memory is when they are subslices of nonzero | ||
// slices | ||
let (opt_ptr, n, ty) = match value.ty.builtin_deref(false).unwrap().ty.sty { | ||
ty::TyKind::Array(t, n) => (value.to_ptr(), n.unwrap_usize(cx.tcx), t), | ||
ty::TyKind::Slice(t) => { | ||
match value.val { | ||
ConstValue::ScalarPair(ptr, n) => ( | ||
ptr.to_ptr().ok(), | ||
n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64, | ||
t, | ||
), | ||
_ => span_bug!( | ||
pat.span, | ||
"slice pattern constant must be scalar pair but is {:?}", | ||
value, | ||
), | ||
} | ||
}, | ||
let (opt_ptr, n, ty) = match (value.val, &value.ty.sty) { | ||
(ConstValue::ByRef(id, alloc, offset), ty::TyKind::Array(t, n)) => ( | ||
Some(( | ||
Pointer::new(id, offset), | ||
alloc, | ||
)), | ||
n.unwrap_usize(cx.tcx), | ||
t, | ||
), | ||
(ConstValue::ScalarPair(ptr, n), ty::TyKind::Slice(t)) => ( | ||
ptr.to_ptr().ok().map(|ptr| ( | ||
ptr, | ||
cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id), | ||
)), | ||
n.to_bits(cx.tcx.data_layout.pointer_size).unwrap() as u64, | ||
t, | ||
), | ||
_ => span_bug!( | ||
pat.span, | ||
"unexpected const-val {:?} with ctor {:?}", | ||
|
@@ -1702,8 +1783,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( | |
// convert a constant slice/array pattern to a list of patterns. | ||
match (n, opt_ptr) { | ||
(0, _) => Some(SmallVec::new()), | ||
(_, Some(ptr)) => { | ||
let alloc = cx.tcx.alloc_map.lock().unwrap_memory(ptr.alloc_id); | ||
(_, Some((ptr, alloc))) => { | ||
let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; | ||
(0..n).map(|i| { | ||
let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; | ||
|
@@ -1766,9 +1846,9 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>( | |
None | ||
} | ||
} | ||
ConstantValue(..) => { | ||
match slice_pat_covered_by_constructor( | ||
cx.tcx, pat.span, constructor, prefix, slice, suffix | ||
ConstantValue(cv) => { | ||
match slice_pat_covered_by_const( | ||
cx.tcx, pat.span, cv, prefix, slice, suffix | ||
) { | ||
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Ok(true) => Some(smallvec![]), | ||
Ok(false) => None, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,9 +28,10 @@ fn main() { | |
_ => panic!("c"), | ||
} | ||
|
||
#[allow(unreachable_patterns)] | ||
match &43 { | ||
&42 => panic!(), | ||
BOO => panic!(), | ||
BOO => panic!(), // pattern is unreachable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a behavioral change of this PR. We now detect that
oli-obk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
_ => println!("d"), | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.