Skip to content

Commit

Permalink
Encode int/float/bool/char patterns directly as ScalarInt
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Jul 12, 2021
1 parent aca10b3 commit 987b9fc
Show file tree
Hide file tree
Showing 27 changed files with 193 additions and 136 deletions.
14 changes: 9 additions & 5 deletions compiler/rustc_middle/src/thir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use rustc_middle::ty::{self, AdtDef, Const, Ty, UpvarSubsts, UserType};
use rustc_middle::ty::{
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
};
use rustc_middle::ty::{ConstInt, ScalarInt};
use rustc_span::{Span, Symbol, DUMMY_SP};
use rustc_target::abi::VariantIdx;
use rustc_target::asm::InlineAsmRegOrRegClass;
Expand Down Expand Up @@ -668,8 +669,9 @@ pub enum PatKind<'tcx> {

#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
pub struct PatRange<'tcx> {
pub lo: &'tcx ty::Const<'tcx>,
pub hi: &'tcx ty::Const<'tcx>,
pub lo: ScalarInt,
pub hi: ScalarInt,
pub ty: Ty<'tcx>,
pub end: RangeEnd,
}

Expand Down Expand Up @@ -788,10 +790,12 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
write!(f, "{}", subpattern)
}
PatKind::Constant { value } => write!(f, "{}", value),
PatKind::Range(PatRange { lo, hi, end }) => {
write!(f, "{}", lo)?;
PatKind::Range(PatRange { lo, hi, end, ty }) => {
let lo = ConstInt::new(lo, ty.is_signed(), ty.is_ptr_sized_integral());
let hi = ConstInt::new(hi, ty.is_signed(), ty.is_ptr_sized_integral());
write!(f, "{:?}", lo)?;
write!(f, "{}", end)?;
write!(f, "{}", hi)
write!(f, "{:?}", hi)
}
PatKind::Slice { ref prefix, ref slice, ref suffix }
| PatKind::Array { ref prefix, ref slice, ref suffix } => {
Expand Down
28 changes: 14 additions & 14 deletions compiler/rustc_mir_build/src/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Err(match_pair)
}

PatKind::Range(PatRange { lo, hi, end }) => {
let (range, bias) = match *lo.ty.kind() {
PatKind::Range(PatRange { lo, hi, end, ty }) => {
let (range, bias) = match *ty.kind() {
ty::Char => {
(Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
}
Expand All @@ -215,18 +215,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
_ => (None, 0),
};
if let Some((min, max, sz)) = range {
if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) {
// We want to compare ranges numerically, but the order of the bitwise
// representation of signed integers does not match their numeric order.
// Thus, to correct the ordering, we need to shift the range of signed
// integers to correct the comparison. This is achieved by XORing with a
// bias (see pattern/_match.rs for another pertinent example of this
// pattern).
let (lo, hi) = (lo ^ bias, hi ^ bias);
if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
// Irrefutable pattern match.
return Ok(());
}
let lo = lo.assert_bits(sz);
let hi = hi.assert_bits(sz);
// We want to compare ranges numerically, but the order of the bitwise
// representation of signed integers does not match their numeric order.
// Thus, to correct the ordering, we need to shift the range of signed
// integers to correct the comparison. This is achieved by XORing with a
// bias (see pattern/_match.rs for another pertinent example of this
// pattern).
let (lo, hi) = (lo ^ bias, hi ^ bias);
if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
// Irrefutable pattern match.
return Ok(());
}
}
Err(match_pair)
Expand Down
28 changes: 20 additions & 8 deletions compiler/rustc_mir_build/src/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::thir::pattern::compare_const_vals;
use rustc_data_structures::fx::FxIndexMap;
use rustc_hir::{LangItem, RangeEnd};
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::subst::{GenericArg, Subst};
Expand Down Expand Up @@ -58,8 +59,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
},

PatKind::Range(range) => {
assert_eq!(range.lo.ty, match_pair.pattern.ty);
assert_eq!(range.hi.ty, match_pair.pattern.ty);
assert_eq!(range.ty, match_pair.pattern.ty);
Test { span: match_pair.pattern.span, kind: TestKind::Range(range) }
}

Expand Down Expand Up @@ -271,11 +271,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

TestKind::Range(PatRange { ref lo, ref hi, ref end }) => {
TestKind::Range(PatRange { lo, hi, ref end, ty }) => {
let lower_bound_success = self.cfg.start_new_block();
let target_blocks = make_target_blocks(self);

// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
let lo = ty::Const::from_value(self.tcx, ConstValue::Scalar(lo.into()), ty);
let hi = ty::Const::from_value(self.tcx, ConstValue::Scalar(hi.into()), ty);
let lo = self.literal_operand(test.span, lo);
let hi = self.literal_operand(test.span, hi);
let val = Operand::Copy(place);
Expand Down Expand Up @@ -640,9 +642,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

let tcx = self.tcx;

let test_ty = test.lo.ty;
let lo = compare_const_vals(tcx, test.lo, pat.hi, self.param_env, test_ty)?;
let hi = compare_const_vals(tcx, test.hi, pat.lo, self.param_env, test_ty)?;
let test_ty = match_pair.pattern.ty;
let test_lo =
ty::Const::from_value(tcx, ConstValue::Scalar(test.lo.into()), test_ty);
let test_hi =
ty::Const::from_value(tcx, ConstValue::Scalar(test.hi.into()), test_ty);
let pat_lo =
ty::Const::from_value(tcx, ConstValue::Scalar(test.lo.into()), test_ty);
let pat_hi =
ty::Const::from_value(tcx, ConstValue::Scalar(test.hi.into()), test_ty);
let lo = compare_const_vals(tcx, test_lo, pat_hi, self.param_env, test_ty)?;
let hi = compare_const_vals(tcx, test_hi, pat_lo, self.param_env, test_ty)?;

match (test.end, pat.end, lo, hi) {
// pat < test
Expand Down Expand Up @@ -769,8 +779,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

let tcx = self.tcx;

let a = compare_const_vals(tcx, range.lo, value, self.param_env, range.lo.ty)?;
let b = compare_const_vals(tcx, value, range.hi, self.param_env, range.lo.ty)?;
let lo = ty::Const::from_value(tcx, ConstValue::Scalar(range.lo.into()), value.ty);
let hi = ty::Const::from_value(tcx, ConstValue::Scalar(range.hi.into()), value.ty);
let a = compare_const_vals(tcx, lo, value, self.param_env, value.ty)?;
let b = compare_const_vals(tcx, value, hi, self.param_env, value.ty)?;

match (b, range.end) {
(Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true),
Expand Down
59 changes: 44 additions & 15 deletions compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ use rustc_data_structures::captures::Captures;
use rustc_index::vec::Idx;

use rustc_hir::{HirId, RangeEnd};
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::mir::Field;
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
use rustc_middle::ty::{self, Const, ScalarInt, Ty, TyCtxt};
use rustc_session::lint;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{Integer, Size, VariantIdx};
Expand Down Expand Up @@ -202,14 +203,18 @@ impl IntRange {
let bias = IntRange::signed_bias(tcx, ty);
let (lo, hi) = (lo ^ bias, hi ^ bias);

let env = ty::ParamEnv::empty().and(ty);
let lo_const = ty::Const::from_bits(tcx, lo, env);
let hi_const = ty::Const::from_bits(tcx, hi, env);

let kind = if lo == hi {
PatKind::Constant { value: lo_const }
let ty = ty::ParamEnv::empty().and(ty);
PatKind::Constant { value: ty::Const::from_bits(tcx, lo, ty) }
} else {
PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included })
let param_env_and_ty = ty::ParamEnv::empty().and(ty);
let size = tcx.layout_of(param_env_and_ty).unwrap().size;
PatKind::Range(PatRange {
lo: ScalarInt::from_uint(lo, size),
hi: ScalarInt::from_uint(hi, size),
end: RangeEnd::Included,
ty,
})
};

Pat { ty, span: DUMMY_SP, kind: Box::new(kind) }
Expand Down Expand Up @@ -586,7 +591,7 @@ pub(super) enum Constructor<'tcx> {
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
IntRange(IntRange),
/// Ranges of floating-point literal values (`2.0..=5.2`).
FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
FloatRange(ScalarInt, ScalarInt, RangeEnd),
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
Str(&'tcx ty::Const<'tcx>),
/// Array and slice patterns.
Expand Down Expand Up @@ -647,7 +652,11 @@ impl<'tcx> Constructor<'tcx> {
IntRange(int_range)
} else {
match pat.ty.kind() {
ty::Float(_) => FloatRange(value, value, RangeEnd::Included),
ty::Float(_) => {
let value =
value.val.eval(cx.tcx, cx.param_env).try_to_scalar_int().unwrap();
FloatRange(value, value, RangeEnd::Included)
}
// In `expand_pattern`, we convert string literals to `&CONST` patterns with
// `CONST` a pattern of type `str`. In truth this contains a constant of type
// `&str`.
Expand All @@ -659,13 +668,13 @@ impl<'tcx> Constructor<'tcx> {
}
}
}
&PatKind::Range(PatRange { lo, hi, end }) => {
let ty = lo.ty;
&PatKind::Range(PatRange { lo, hi, end, ty }) => {
let size = cx.tcx.layout_of(cx.param_env.and(ty)).unwrap().size;
if let Some(int_range) = IntRange::from_range(
cx.tcx,
lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
ty,
lo.assert_bits(size),
hi.assert_bits(size),
pat.ty,
&end,
) {
IntRange(int_range)
Expand Down Expand Up @@ -759,6 +768,26 @@ impl<'tcx> Constructor<'tcx> {
FloatRange(self_from, self_to, self_end),
FloatRange(other_from, other_to, other_end),
) => {
let self_to = ty::Const::from_value(
pcx.cx.tcx,
ConstValue::Scalar((*self_to).into()),
pcx.ty,
);
let other_to = ty::Const::from_value(
pcx.cx.tcx,
ConstValue::Scalar((*other_to).into()),
pcx.ty,
);
let self_from = ty::Const::from_value(
pcx.cx.tcx,
ConstValue::Scalar((*self_from).into()),
pcx.ty,
);
let other_from = ty::Const::from_value(
pcx.cx.tcx,
ConstValue::Scalar((*other_from).into()),
pcx.ty,
);
match (
compare_const_vals(pcx.cx.tcx, self_to, other_to, pcx.cx.param_env, pcx.ty),
compare_const_vals(pcx.cx.tcx, self_from, other_from, pcx.cx.param_env, pcx.ty),
Expand Down Expand Up @@ -1253,7 +1282,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
}
},
&Str(value) => PatKind::Constant { value },
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end, ty: pcx.ty }),
IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty),
NonExhaustive => PatKind::Wild,
Wildcard => return Pat::wildcard_from_ty(pcx.ty),
Expand Down
21 changes: 18 additions & 3 deletions compiler/rustc_mir_build/src/thir/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,23 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
assert_eq!(lo.ty, ty);
assert_eq!(hi.ty, ty);
let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty);
let lo_const = lo;
let lo = lo
.val
.eval(self.tcx, self.param_env)
.try_to_scalar_int()
.expect("range patterns must be integral");
let hi = hi
.val
.eval(self.tcx, self.param_env)
.try_to_scalar_int()
.expect("range patterns must be integral");
match (end, cmp) {
// `x..y` where `x < y`.
// Non-empty because the range includes at least `x`.
(RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
(RangeEnd::Excluded, Some(Ordering::Less)) => {
PatKind::Range(PatRange { lo, hi, end, ty })
}
// `x..y` where `x >= y`. The range is empty => error.
(RangeEnd::Excluded, _) => {
struct_span_err!(
Expand All @@ -146,9 +159,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
PatKind::Wild
}
// `x..=y` where `x == y`.
(RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo },
(RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo_const },
// `x..=y` where `x < y`.
(RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
(RangeEnd::Included, Some(Ordering::Less)) => {
PatKind::Range(PatRange { lo, hi, end, ty })
}
// `x..=y` where `x > y` hence the range is empty => error.
(RangeEnd::Included, _) => {
let mut err = struct_span_err!(
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_mir_build/src/thir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,7 @@ pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<'
}
}
Constant { value } => visitor.visit_const(value),
Range(range) => {
visitor.visit_const(range.lo);
visitor.visit_const(range.hi);
}
Range(PatRange { lo: _, hi: _, ty: _, end: _ }) => {}
Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => {
for subpattern in prefix {
visitor.visit_pat(&subpattern);
Expand Down
26 changes: 13 additions & 13 deletions src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir
Original file line number Diff line number Diff line change
Expand Up @@ -28,43 +28,43 @@ fn main() -> () {
StorageLive(_3); // scope 2 at $DIR/match_test.rs:12:5: 17:6
FakeRead(ForMatchedPlace(None), _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12
_6 = Le(const 0_i32, _1); // scope 2 at $DIR/match_test.rs:13:9: 13:14
switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
switchInt(move _6) -> [false: bb3, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
}

bb1: {
_7 = Lt(_1, const 10_i32); // scope 2 at $DIR/match_test.rs:13:9: 13:14
switchInt(move _7) -> [false: bb4, otherwise: bb2]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
switchInt(move _7) -> [false: bb3, otherwise: bb2]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
}

bb2: {
falseEdge -> [real: bb9, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
falseEdge -> [real: bb9, imaginary: bb5]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
}

bb3: {
_3 = const 3_i32; // scope 2 at $DIR/match_test.rs:16:14: 16:15
goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6
_4 = Le(const 10_i32, _1); // scope 2 at $DIR/match_test.rs:14:9: 14:16
switchInt(move _4) -> [false: bb6, otherwise: bb4]; // scope 2 at $DIR/match_test.rs:14:9: 14:16
}

bb4: {
_4 = Le(const 10_i32, _1); // scope 2 at $DIR/match_test.rs:14:9: 14:16
switchInt(move _4) -> [false: bb7, otherwise: bb5]; // scope 2 at $DIR/match_test.rs:14:9: 14:16
_5 = Le(_1, const 20_i32); // scope 2 at $DIR/match_test.rs:14:9: 14:16
switchInt(move _5) -> [false: bb6, otherwise: bb5]; // scope 2 at $DIR/match_test.rs:14:9: 14:16
}

bb5: {
_5 = Le(_1, const 20_i32); // scope 2 at $DIR/match_test.rs:14:9: 14:16
switchInt(move _5) -> [false: bb7, otherwise: bb6]; // scope 2 at $DIR/match_test.rs:14:9: 14:16
falseEdge -> [real: bb12, imaginary: bb7]; // scope 2 at $DIR/match_test.rs:14:9: 14:16
}

bb6: {
falseEdge -> [real: bb12, imaginary: bb8]; // scope 2 at $DIR/match_test.rs:14:9: 14:16
switchInt(_1) -> [-1_i32: bb7, otherwise: bb8]; // scope 2 at $DIR/match_test.rs:15:9: 15:11
}

bb7: {
switchInt(_1) -> [-1_i32: bb8, otherwise: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11
falseEdge -> [real: bb13, imaginary: bb8]; // scope 2 at $DIR/match_test.rs:15:9: 15:11
}

bb8: {
falseEdge -> [real: bb13, imaginary: bb3]; // scope 2 at $DIR/match_test.rs:15:9: 15:11
_3 = const 3_i32; // scope 2 at $DIR/match_test.rs:16:14: 16:15
goto -> bb14; // scope 2 at $DIR/match_test.rs:12:5: 17:6
}

bb9: {
Expand All @@ -83,7 +83,7 @@ fn main() -> () {

bb11: {
StorageDead(_9); // scope 2 at $DIR/match_test.rs:13:23: 13:24
falseEdge -> [real: bb3, imaginary: bb6]; // scope 2 at $DIR/match_test.rs:13:18: 13:19
falseEdge -> [real: bb3, imaginary: bb5]; // scope 2 at $DIR/match_test.rs:13:18: 13:19
}

bb12: {
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/const-match-check.eval1.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1` and `1..=i32::MAX` not covered
--> $DIR/const-match-check.rs:25:15
|
LL | A = { let 0 = 0; 0 },
| ^ patterns `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
| ^ patterns `i32::MIN..=-1` and `1..=i32::MAX` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/consts/const-match-check.eval2.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1` and `1..=i32::MAX` not covered
--> $DIR/const-match-check.rs:31:24
|
LL | let x: [i32; { let 0 = 0; 0 }] = [];
| ^ patterns `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
| ^ patterns `i32::MIN..=-1` and `1..=i32::MAX` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
Expand Down
Loading

0 comments on commit 987b9fc

Please sign in to comment.