Skip to content

Commit a9f2c5a

Browse files
committed
Fix sign conversion arithmetic errors
1 parent f4af3b0 commit a9f2c5a

File tree

2 files changed

+44
-33
lines changed

2 files changed

+44
-33
lines changed

src/librustc_mir/hair/pattern/_match.rs

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,9 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
471471
}
472472
ty::TyInt(_) if exhaustive_integer_patterns => {
473473
let size = cx.tcx.layout_of(ty::ParamEnv::reveal_all().and(pcx.ty))
474-
.unwrap().size.bits() as i128;
475-
let min = (1i128 << (size - 1)).wrapping_neg();
476-
let max = (1i128 << (size - 1)).wrapping_sub(1);
474+
.unwrap().size.bits() as u128;
475+
let min = (1u128 << (size - 1)).wrapping_neg();
476+
let max = (1u128 << (size - 1)).wrapping_sub(1);
477477
value_constructors = true;
478478
vec![ConstantRange(ty::Const::from_bits(cx.tcx, min as u128, pcx.ty),
479479
ty::Const::from_bits(cx.tcx, max as u128, pcx.ty),
@@ -603,13 +603,17 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
603603
}
604604

605605
/// An inclusive interval, used for precise integer exhaustiveness checking.
606+
/// `Interval`s always store a contiguous range of integers. That means that
607+
/// signed integers are offset (see `offset_sign`) by their minimum value.
606608
struct Interval<'tcx> {
607609
pub range: RangeInclusive<u128>,
608610
pub ty: Ty<'tcx>,
609611
}
610612

611613
impl<'tcx> Interval<'tcx> {
612-
fn from_ctor(ctor: &Constructor<'tcx>) -> Option<Interval<'tcx>> {
614+
fn from_ctor(tcx: TyCtxt<'_, 'tcx, 'tcx>,
615+
ctor: &Constructor<'tcx>)
616+
-> Option<Interval<'tcx>> {
613617
match ctor {
614618
ConstantRange(lo, hi, end) => {
615619
assert_eq!(lo.ty, hi.ty);
@@ -618,7 +622,7 @@ impl<'tcx> Interval<'tcx> {
618622
if let Some(hi) = hi.assert_bits(ty) {
619623
// Perform a shift if the underlying types are signed,
620624
// which makes the interval arithmetic simpler.
621-
let (lo, hi) = Interval::offset_sign(ty, lo..=hi, true);
625+
let (lo, hi) = Self::offset_sign(tcx, ty, lo..=hi, true);
622626
// Make sure the interval is well-formed.
623627
return if lo > hi || lo == hi && *end == RangeEnd::Excluded {
624628
None
@@ -632,38 +636,45 @@ impl<'tcx> Interval<'tcx> {
632636
}
633637
ConstantValue(val) => {
634638
let ty = val.ty;
635-
val.assert_bits(ty).map(|val| Interval { range: val..=val, ty })
639+
if let Some(val) = val.assert_bits(ty) {
640+
let (lo, hi) = Self::offset_sign(tcx, ty, val..=val, true);
641+
Some(Interval { range: lo..=hi, ty })
642+
} else {
643+
None
644+
}
636645
}
637646
Single | Variant(_) | Slice(_) => {
638647
None
639648
}
640649
}
641650
}
642651

643-
fn offset_sign(ty: Ty<'tcx>, range: RangeInclusive<u128>, forwards: bool) -> (u128, u128) {
652+
fn offset_sign(tcx: TyCtxt<'_, 'tcx, 'tcx>,
653+
ty: Ty<'tcx>,
654+
range: RangeInclusive<u128>,
655+
encode: bool)
656+
-> (u128, u128) {
657+
// We ensure that all integer values are contiguous: that is, that their
658+
// minimum value is represented by 0, so that comparisons and increments/
659+
// decrements on interval endpoints work consistently whether the endpoints
660+
// are signed or unsigned.
644661
let (lo, hi) = range.into_inner();
645-
use syntax::ast::IntTy::*;
646662
match ty.sty {
647-
ty::TyInt(int_ty) => {
648-
macro_rules! offset_sign_for_ty {
649-
($ity:ident, $uty:ty) => {{
650-
let min = Wrapping($ity::MIN as $uty);
651-
if forwards {
652-
((Wrapping(lo as $uty) + min).0 as u128,
653-
(Wrapping(hi as $uty) + min).0 as u128)
654-
} else {
655-
((Wrapping(lo as $uty) + min).0 as $ity as u128,
656-
(Wrapping(hi as $uty) + min).0 as $ity as u128)
657-
}
658-
}}
659-
}
660-
match int_ty {
661-
Isize => offset_sign_for_ty!(isize, usize),
662-
I8 => offset_sign_for_ty!(i8, u8),
663-
I16 => offset_sign_for_ty!(i16, u16),
664-
I32 => offset_sign_for_ty!(i32, u32),
665-
I64 => offset_sign_for_ty!(i64, u64),
666-
I128 => offset_sign_for_ty!(i128, u128),
663+
ty::TyInt(_) => {
664+
let size = tcx.layout_of(ty::ParamEnv::reveal_all().and(ty))
665+
.unwrap().size.bits() as u128;
666+
let min = (1u128 << (size - 1)).wrapping_neg();
667+
let shift = 1u128.overflowing_shl(size as u32);
668+
let mask = shift.0.wrapping_sub(1 + (shift.1 as u128));
669+
if encode {
670+
let offset = |x: u128| x.wrapping_sub(min) & mask;
671+
(offset(lo), offset(hi))
672+
} else {
673+
let offset = |x: u128| {
674+
interpret::sign_extend(tcx, x.wrapping_add(min) & mask, ty)
675+
.expect("layout error for TyInt")
676+
};
677+
(offset(lo), offset(hi))
667678
}
668679
}
669680
ty::TyUint(_) | ty::TyChar => {
@@ -685,10 +696,10 @@ fn ranges_subtract_pattern<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
685696
pat_ctor: &Constructor<'tcx>,
686697
ranges: Vec<Constructor<'tcx>>)
687698
-> Vec<Constructor<'tcx>> {
688-
if let Some(pat_interval) = Interval::from_ctor(pat_ctor) {
699+
if let Some(pat_interval) = Interval::from_ctor(cx.tcx, pat_ctor) {
689700
let mut remaining_ranges = vec![];
690701
let mut ranges: Vec<_> = ranges.into_iter().filter_map(|r| {
691-
Interval::from_ctor(&r).map(|i| i.into_inner())
702+
Interval::from_ctor(cx.tcx, &r).map(|i| i.into_inner())
692703
}).collect();
693704
let ty = pat_interval.ty;
694705
let (pat_interval_lo, pat_interval_hi) = pat_interval.into_inner();
@@ -712,7 +723,7 @@ fn ranges_subtract_pattern<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
712723
}
713724
// Convert the remaining ranges from pairs to inclusive `ConstantRange`s.
714725
remaining_ranges.into_iter().map(|r| {
715-
let (lo, hi) = Interval::offset_sign(ty, r, false);
726+
let (lo, hi) = Interval::offset_sign(cx.tcx, ty, r, false);
716727
ConstantRange(ty::Const::from_bits(cx.tcx, lo, ty),
717728
ty::Const::from_bits(cx.tcx, hi, ty),
718729
RangeEnd::Included)

src/test/ui/exhaustive_integer_patterns.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ error: unreachable pattern
2828
LL | -2..=20 => {} //~ ERROR unreachable pattern
2929
| ^^^^^^^
3030

31-
error[E0004]: non-exhaustive patterns: `-128i8...-6i8` and `122i8...127i8` not covered
31+
error[E0004]: non-exhaustive patterns: `-128i8...-8i8`, `-6i8`, `121i8...124i8` and 1 more not covered
3232
--> $DIR/exhaustive_integer_patterns.rs:50:11
3333
|
3434
LL | match x { //~ ERROR non-exhaustive patterns
35-
| ^ patterns `-128i8...-6i8` and `122i8...127i8` not covered
35+
| ^ patterns `-128i8...-8i8`, `-6i8`, `121i8...124i8` and 1 more not covered
3636

3737
error[E0004]: non-exhaustive patterns: `-128i8` not covered
3838
--> $DIR/exhaustive_integer_patterns.rs:99:11

0 commit comments

Comments
 (0)