Skip to content

Commit 987b9fc

Browse files
committed
Encode int/float/bool/char patterns directly as ScalarInt
1 parent aca10b3 commit 987b9fc

27 files changed

+193
-136
lines changed

compiler/rustc_middle/src/thir.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use rustc_middle::ty::{self, AdtDef, Const, Ty, UpvarSubsts, UserType};
2626
use rustc_middle::ty::{
2727
CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
2828
};
29+
use rustc_middle::ty::{ConstInt, ScalarInt};
2930
use rustc_span::{Span, Symbol, DUMMY_SP};
3031
use rustc_target::abi::VariantIdx;
3132
use rustc_target::asm::InlineAsmRegOrRegClass;
@@ -668,8 +669,9 @@ pub enum PatKind<'tcx> {
668669

669670
#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
670671
pub struct PatRange<'tcx> {
671-
pub lo: &'tcx ty::Const<'tcx>,
672-
pub hi: &'tcx ty::Const<'tcx>,
672+
pub lo: ScalarInt,
673+
pub hi: ScalarInt,
674+
pub ty: Ty<'tcx>,
673675
pub end: RangeEnd,
674676
}
675677

@@ -788,10 +790,12 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
788790
write!(f, "{}", subpattern)
789791
}
790792
PatKind::Constant { value } => write!(f, "{}", value),
791-
PatKind::Range(PatRange { lo, hi, end }) => {
792-
write!(f, "{}", lo)?;
793+
PatKind::Range(PatRange { lo, hi, end, ty }) => {
794+
let lo = ConstInt::new(lo, ty.is_signed(), ty.is_ptr_sized_integral());
795+
let hi = ConstInt::new(hi, ty.is_signed(), ty.is_ptr_sized_integral());
796+
write!(f, "{:?}", lo)?;
793797
write!(f, "{}", end)?;
794-
write!(f, "{}", hi)
798+
write!(f, "{:?}", hi)
795799
}
796800
PatKind::Slice { ref prefix, ref slice, ref suffix }
797801
| PatKind::Array { ref prefix, ref slice, ref suffix } => {

compiler/rustc_mir_build/src/build/matches/simplify.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
196196
Err(match_pair)
197197
}
198198

199-
PatKind::Range(PatRange { lo, hi, end }) => {
200-
let (range, bias) = match *lo.ty.kind() {
199+
PatKind::Range(PatRange { lo, hi, end, ty }) => {
200+
let (range, bias) = match *ty.kind() {
201201
ty::Char => {
202202
(Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
203203
}
@@ -215,18 +215,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
215215
_ => (None, 0),
216216
};
217217
if let Some((min, max, sz)) = range {
218-
if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) {
219-
// We want to compare ranges numerically, but the order of the bitwise
220-
// representation of signed integers does not match their numeric order.
221-
// Thus, to correct the ordering, we need to shift the range of signed
222-
// integers to correct the comparison. This is achieved by XORing with a
223-
// bias (see pattern/_match.rs for another pertinent example of this
224-
// pattern).
225-
let (lo, hi) = (lo ^ bias, hi ^ bias);
226-
if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
227-
// Irrefutable pattern match.
228-
return Ok(());
229-
}
218+
let lo = lo.assert_bits(sz);
219+
let hi = hi.assert_bits(sz);
220+
// We want to compare ranges numerically, but the order of the bitwise
221+
// representation of signed integers does not match their numeric order.
222+
// Thus, to correct the ordering, we need to shift the range of signed
223+
// integers to correct the comparison. This is achieved by XORing with a
224+
// bias (see pattern/_match.rs for another pertinent example of this
225+
// pattern).
226+
let (lo, hi) = (lo ^ bias, hi ^ bias);
227+
if lo <= min && (hi > max || hi == max && end == RangeEnd::Included) {
228+
// Irrefutable pattern match.
229+
return Ok(());
230230
}
231231
}
232232
Err(match_pair)

compiler/rustc_mir_build/src/build/matches/test.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::thir::pattern::compare_const_vals;
1212
use rustc_data_structures::fx::FxIndexMap;
1313
use rustc_hir::{LangItem, RangeEnd};
1414
use rustc_index::bit_set::BitSet;
15+
use rustc_middle::mir::interpret::ConstValue;
1516
use rustc_middle::mir::*;
1617
use rustc_middle::thir::*;
1718
use rustc_middle::ty::subst::{GenericArg, Subst};
@@ -58,8 +59,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
5859
},
5960

6061
PatKind::Range(range) => {
61-
assert_eq!(range.lo.ty, match_pair.pattern.ty);
62-
assert_eq!(range.hi.ty, match_pair.pattern.ty);
62+
assert_eq!(range.ty, match_pair.pattern.ty);
6363
Test { span: match_pair.pattern.span, kind: TestKind::Range(range) }
6464
}
6565

@@ -271,11 +271,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
271271
}
272272
}
273273

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

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

641643
let tcx = self.tcx;
642644

643-
let test_ty = test.lo.ty;
644-
let lo = compare_const_vals(tcx, test.lo, pat.hi, self.param_env, test_ty)?;
645-
let hi = compare_const_vals(tcx, test.hi, pat.lo, self.param_env, test_ty)?;
645+
let test_ty = match_pair.pattern.ty;
646+
let test_lo =
647+
ty::Const::from_value(tcx, ConstValue::Scalar(test.lo.into()), test_ty);
648+
let test_hi =
649+
ty::Const::from_value(tcx, ConstValue::Scalar(test.hi.into()), test_ty);
650+
let pat_lo =
651+
ty::Const::from_value(tcx, ConstValue::Scalar(test.lo.into()), test_ty);
652+
let pat_hi =
653+
ty::Const::from_value(tcx, ConstValue::Scalar(test.hi.into()), test_ty);
654+
let lo = compare_const_vals(tcx, test_lo, pat_hi, self.param_env, test_ty)?;
655+
let hi = compare_const_vals(tcx, test_hi, pat_lo, self.param_env, test_ty)?;
646656

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

770780
let tcx = self.tcx;
771781

772-
let a = compare_const_vals(tcx, range.lo, value, self.param_env, range.lo.ty)?;
773-
let b = compare_const_vals(tcx, value, range.hi, self.param_env, range.lo.ty)?;
782+
let lo = ty::Const::from_value(tcx, ConstValue::Scalar(range.lo.into()), value.ty);
783+
let hi = ty::Const::from_value(tcx, ConstValue::Scalar(range.hi.into()), value.ty);
784+
let a = compare_const_vals(tcx, lo, value, self.param_env, value.ty)?;
785+
let b = compare_const_vals(tcx, value, hi, self.param_env, value.ty)?;
774786

775787
match (b, range.end) {
776788
(Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true),

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,11 @@ use rustc_data_structures::captures::Captures;
5252
use rustc_index::vec::Idx;
5353

5454
use rustc_hir::{HirId, RangeEnd};
55+
use rustc_middle::mir::interpret::ConstValue;
5556
use rustc_middle::mir::Field;
5657
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
5758
use rustc_middle::ty::layout::IntegerExt;
58-
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
59+
use rustc_middle::ty::{self, Const, ScalarInt, Ty, TyCtxt};
5960
use rustc_session::lint;
6061
use rustc_span::{Span, DUMMY_SP};
6162
use rustc_target::abi::{Integer, Size, VariantIdx};
@@ -202,14 +203,18 @@ impl IntRange {
202203
let bias = IntRange::signed_bias(tcx, ty);
203204
let (lo, hi) = (lo ^ bias, hi ^ bias);
204205

205-
let env = ty::ParamEnv::empty().and(ty);
206-
let lo_const = ty::Const::from_bits(tcx, lo, env);
207-
let hi_const = ty::Const::from_bits(tcx, hi, env);
208-
209206
let kind = if lo == hi {
210-
PatKind::Constant { value: lo_const }
207+
let ty = ty::ParamEnv::empty().and(ty);
208+
PatKind::Constant { value: ty::Const::from_bits(tcx, lo, ty) }
211209
} else {
212-
PatKind::Range(PatRange { lo: lo_const, hi: hi_const, end: RangeEnd::Included })
210+
let param_env_and_ty = ty::ParamEnv::empty().and(ty);
211+
let size = tcx.layout_of(param_env_and_ty).unwrap().size;
212+
PatKind::Range(PatRange {
213+
lo: ScalarInt::from_uint(lo, size),
214+
hi: ScalarInt::from_uint(hi, size),
215+
end: RangeEnd::Included,
216+
ty,
217+
})
213218
};
214219

215220
Pat { ty, span: DUMMY_SP, kind: Box::new(kind) }
@@ -586,7 +591,7 @@ pub(super) enum Constructor<'tcx> {
586591
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
587592
IntRange(IntRange),
588593
/// Ranges of floating-point literal values (`2.0..=5.2`).
589-
FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
594+
FloatRange(ScalarInt, ScalarInt, RangeEnd),
590595
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
591596
Str(&'tcx ty::Const<'tcx>),
592597
/// Array and slice patterns.
@@ -647,7 +652,11 @@ impl<'tcx> Constructor<'tcx> {
647652
IntRange(int_range)
648653
} else {
649654
match pat.ty.kind() {
650-
ty::Float(_) => FloatRange(value, value, RangeEnd::Included),
655+
ty::Float(_) => {
656+
let value =
657+
value.val.eval(cx.tcx, cx.param_env).try_to_scalar_int().unwrap();
658+
FloatRange(value, value, RangeEnd::Included)
659+
}
651660
// In `expand_pattern`, we convert string literals to `&CONST` patterns with
652661
// `CONST` a pattern of type `str`. In truth this contains a constant of type
653662
// `&str`.
@@ -659,13 +668,13 @@ impl<'tcx> Constructor<'tcx> {
659668
}
660669
}
661670
}
662-
&PatKind::Range(PatRange { lo, hi, end }) => {
663-
let ty = lo.ty;
671+
&PatKind::Range(PatRange { lo, hi, end, ty }) => {
672+
let size = cx.tcx.layout_of(cx.param_env.and(ty)).unwrap().size;
664673
if let Some(int_range) = IntRange::from_range(
665674
cx.tcx,
666-
lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
667-
hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
668-
ty,
675+
lo.assert_bits(size),
676+
hi.assert_bits(size),
677+
pat.ty,
669678
&end,
670679
) {
671680
IntRange(int_range)
@@ -759,6 +768,26 @@ impl<'tcx> Constructor<'tcx> {
759768
FloatRange(self_from, self_to, self_end),
760769
FloatRange(other_from, other_to, other_end),
761770
) => {
771+
let self_to = ty::Const::from_value(
772+
pcx.cx.tcx,
773+
ConstValue::Scalar((*self_to).into()),
774+
pcx.ty,
775+
);
776+
let other_to = ty::Const::from_value(
777+
pcx.cx.tcx,
778+
ConstValue::Scalar((*other_to).into()),
779+
pcx.ty,
780+
);
781+
let self_from = ty::Const::from_value(
782+
pcx.cx.tcx,
783+
ConstValue::Scalar((*self_from).into()),
784+
pcx.ty,
785+
);
786+
let other_from = ty::Const::from_value(
787+
pcx.cx.tcx,
788+
ConstValue::Scalar((*other_from).into()),
789+
pcx.ty,
790+
);
762791
match (
763792
compare_const_vals(pcx.cx.tcx, self_to, other_to, pcx.cx.param_env, pcx.ty),
764793
compare_const_vals(pcx.cx.tcx, self_from, other_from, pcx.cx.param_env, pcx.ty),
@@ -1253,7 +1282,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
12531282
}
12541283
},
12551284
&Str(value) => PatKind::Constant { value },
1256-
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
1285+
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end, ty: pcx.ty }),
12571286
IntRange(range) => return range.to_pat(pcx.cx.tcx, pcx.ty),
12581287
NonExhaustive => PatKind::Wild,
12591288
Wildcard => return Pat::wildcard_from_ty(pcx.ty),

compiler/rustc_mir_build/src/thir/pattern/mod.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,23 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
130130
assert_eq!(lo.ty, ty);
131131
assert_eq!(hi.ty, ty);
132132
let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty);
133+
let lo_const = lo;
134+
let lo = lo
135+
.val
136+
.eval(self.tcx, self.param_env)
137+
.try_to_scalar_int()
138+
.expect("range patterns must be integral");
139+
let hi = hi
140+
.val
141+
.eval(self.tcx, self.param_env)
142+
.try_to_scalar_int()
143+
.expect("range patterns must be integral");
133144
match (end, cmp) {
134145
// `x..y` where `x < y`.
135146
// Non-empty because the range includes at least `x`.
136-
(RangeEnd::Excluded, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
147+
(RangeEnd::Excluded, Some(Ordering::Less)) => {
148+
PatKind::Range(PatRange { lo, hi, end, ty })
149+
}
137150
// `x..y` where `x >= y`. The range is empty => error.
138151
(RangeEnd::Excluded, _) => {
139152
struct_span_err!(
@@ -146,9 +159,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
146159
PatKind::Wild
147160
}
148161
// `x..=y` where `x == y`.
149-
(RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo },
162+
(RangeEnd::Included, Some(Ordering::Equal)) => PatKind::Constant { value: lo_const },
150163
// `x..=y` where `x < y`.
151-
(RangeEnd::Included, Some(Ordering::Less)) => PatKind::Range(PatRange { lo, hi, end }),
164+
(RangeEnd::Included, Some(Ordering::Less)) => {
165+
PatKind::Range(PatRange { lo, hi, end, ty })
166+
}
152167
// `x..=y` where `x > y` hence the range is empty => error.
153168
(RangeEnd::Included, _) => {
154169
let mut err = struct_span_err!(

compiler/rustc_mir_build/src/thir/visit.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,7 @@ pub fn walk_pat<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, pat: &Pat<'
213213
}
214214
}
215215
Constant { value } => visitor.visit_const(value),
216-
Range(range) => {
217-
visitor.visit_const(range.lo);
218-
visitor.visit_const(range.hi);
219-
}
216+
Range(PatRange { lo: _, hi: _, ty: _, end: _ }) => {}
220217
Slice { prefix, slice, suffix } | Array { prefix, slice, suffix } => {
221218
for subpattern in prefix {
222219
visitor.visit_pat(&subpattern);

src/test/mir-opt/match_test.main.SimplifyCfg-initial.after.mir

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,43 +28,43 @@ fn main() -> () {
2828
StorageLive(_3); // scope 2 at $DIR/match_test.rs:12:5: 17:6
2929
FakeRead(ForMatchedPlace(None), _1); // scope 2 at $DIR/match_test.rs:12:11: 12:12
3030
_6 = Le(const 0_i32, _1); // scope 2 at $DIR/match_test.rs:13:9: 13:14
31-
switchInt(move _6) -> [false: bb4, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
31+
switchInt(move _6) -> [false: bb3, otherwise: bb1]; // scope 2 at $DIR/match_test.rs:13:9: 13:14
3232
}
3333

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

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

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

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

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

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

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

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

7070
bb9: {
@@ -83,7 +83,7 @@ fn main() -> () {
8383

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

8989
bb12: {

src/test/ui/consts/const-match-check.eval1.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
1+
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1` and `1..=i32::MAX` not covered
22
--> $DIR/const-match-check.rs:25:15
33
|
44
LL | A = { let 0 = 0; 0 },
5-
| ^ patterns `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
5+
| ^ patterns `i32::MIN..=-1` and `1..=i32::MAX` not covered
66
|
77
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
88
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html

src/test/ui/consts/const-match-check.eval2.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
1+
error[E0005]: refutable pattern in local binding: `i32::MIN..=-1` and `1..=i32::MAX` not covered
22
--> $DIR/const-match-check.rs:31:24
33
|
44
LL | let x: [i32; { let 0 = 0; 0 }] = [];
5-
| ^ patterns `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered
5+
| ^ patterns `i32::MIN..=-1` and `1..=i32::MAX` not covered
66
|
77
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
88
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html

0 commit comments

Comments
 (0)