Skip to content

Commit 0a6d794

Browse files
committed
Cleanup number literal evaluation
1 parent d6e9b32 commit 0a6d794

File tree

1 file changed

+102
-91
lines changed

1 file changed

+102
-91
lines changed

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

+102-91
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ use rustc_middle::ty::layout::IntegerExt;
6161
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
6262
use rustc_session::lint;
6363
use rustc_span::{Span, DUMMY_SP};
64-
use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT};
64+
use rustc_target::abi::{FieldIdx, Integer, Primitive, Size, VariantIdx, FIRST_VARIANT};
6565

6666
use self::Constructor::*;
6767
use self::SliceKind::*;
@@ -86,6 +86,35 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
8686
pats
8787
}
8888

89+
/// Evaluate an int constant, with a faster branch for a common case.
90+
#[inline]
91+
fn fast_try_eval_bits<'tcx>(
92+
tcx: TyCtxt<'tcx>,
93+
param_env: ty::ParamEnv<'tcx>,
94+
value: &mir::Const<'tcx>,
95+
) -> Option<u128> {
96+
let int = match value {
97+
// If the constant is already evaluated, we shortcut here.
98+
mir::Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => {
99+
valtree.unwrap_leaf()
100+
},
101+
// This is a more general form of the previous case.
102+
_ => {
103+
value.try_eval_scalar_int(tcx, param_env)?
104+
},
105+
};
106+
let size = match value.ty().kind() {
107+
ty::Bool => Size::from_bytes(1),
108+
ty::Char => Size::from_bytes(4),
109+
ty::Int(ity) => Integer::from_int_ty(&tcx, *ity).size(),
110+
ty::Uint(uty) => Integer::from_uint_ty(&tcx, *uty).size(),
111+
ty::Float(ty::FloatTy::F32) => Primitive::F32.size(&tcx),
112+
ty::Float(ty::FloatTy::F64) => Primitive::F64.size(&tcx),
113+
_ => return None,
114+
};
115+
int.to_bits(size).ok()
116+
}
117+
89118
/// An inclusive interval, used for precise integer exhaustiveness checking.
90119
/// `IntRange`s always store a contiguous range. This means that values are
91120
/// encoded such that `0` encodes the minimum value for the integer,
@@ -116,37 +145,12 @@ impl IntRange {
116145
}
117146

118147
#[inline]
119-
fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Size, u128)> {
120-
match *ty.kind() {
121-
ty::Bool => Some((Size::from_bytes(1), 0)),
122-
ty::Char => Some((Size::from_bytes(4), 0)),
123-
ty::Int(ity) => {
124-
let size = Integer::from_int_ty(&tcx, ity).size();
125-
Some((size, 1u128 << (size.bits() as u128 - 1)))
126-
}
127-
ty::Uint(uty) => Some((Integer::from_uint_ty(&tcx, uty).size(), 0)),
128-
_ => None,
129-
}
130-
}
131-
132-
#[inline]
133-
fn from_constant<'tcx>(
134-
tcx: TyCtxt<'tcx>,
135-
param_env: ty::ParamEnv<'tcx>,
136-
value: mir::Const<'tcx>,
137-
) -> Option<IntRange> {
138-
let ty = value.ty();
139-
let (target_size, bias) = Self::integral_size_and_signed_bias(tcx, ty)?;
140-
let val = match value {
141-
mir::Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => {
142-
valtree.unwrap_leaf().to_bits(target_size).ok()
143-
},
144-
// This is a more general form of the previous case.
145-
_ => value.try_eval_bits(tcx, param_env),
146-
}?;
147-
148-
let val = val ^ bias;
149-
Some(IntRange { range: val..=val })
148+
fn from_bits<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, bits: u128) -> IntRange {
149+
let bias = IntRange::signed_bias(tcx, ty);
150+
// Perform a shift if the underlying types are signed,
151+
// which makes the interval arithmetic simpler.
152+
let val = bits ^ bias;
153+
IntRange { range: val..=val }
150154
}
151155

152156
#[inline]
@@ -155,20 +159,18 @@ impl IntRange {
155159
lo: u128,
156160
hi: u128,
157161
ty: Ty<'tcx>,
158-
end: &RangeEnd,
159-
) -> Option<IntRange> {
160-
Self::is_integral(ty).then(|| {
161-
// Perform a shift if the underlying types are signed,
162-
// which makes the interval arithmetic simpler.
163-
let bias = IntRange::signed_bias(tcx, ty);
164-
let (lo, hi) = (lo ^ bias, hi ^ bias);
165-
let offset = (*end == RangeEnd::Excluded) as u128;
166-
if lo > hi || (lo == hi && *end == RangeEnd::Excluded) {
167-
// This should have been caught earlier by E0030.
168-
bug!("malformed range pattern: {}..={}", lo, (hi - offset));
169-
}
170-
IntRange { range: lo..=(hi - offset) }
171-
})
162+
end: RangeEnd,
163+
) -> IntRange {
164+
// Perform a shift if the underlying types are signed,
165+
// which makes the interval arithmetic simpler.
166+
let bias = IntRange::signed_bias(tcx, ty);
167+
let (lo, hi) = (lo ^ bias, hi ^ bias);
168+
let offset = (end == RangeEnd::Excluded) as u128;
169+
if lo > hi || (lo == hi && end == RangeEnd::Excluded) {
170+
// This should have been caught earlier by E0030.
171+
bug!("malformed range pattern: {}..={}", lo, (hi - offset));
172+
}
173+
IntRange { range: lo..=(hi - offset) }
172174
}
173175

174176
// The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
@@ -894,7 +896,7 @@ impl<'tcx> SplitWildcard<'tcx> {
894896
let make_range = |start, end| {
895897
IntRange(
896898
// `unwrap()` is ok because we know the type is an integer.
897-
IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(),
899+
IntRange::from_range(cx.tcx, start, end, pcx.ty, RangeEnd::Included),
898900
)
899901
};
900902
// This determines the set of all possible constructors for the type `pcx.ty`. For numbers,
@@ -1342,57 +1344,66 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
13421344
}
13431345
}
13441346
PatKind::Constant { value } => {
1345-
if let Some(int_range) = IntRange::from_constant(cx.tcx, cx.param_env, *value) {
1346-
ctor = IntRange(int_range);
1347-
fields = Fields::empty();
1348-
} else {
1349-
match pat.ty.kind() {
1350-
ty::Float(float_ty) => {
1351-
let bits = value.eval_bits(cx.tcx, cx.param_env);
1352-
use rustc_apfloat::Float;
1353-
ctor = match float_ty {
1354-
ty::FloatTy::F32 => {
1355-
let value = rustc_apfloat::ieee::Single::from_bits(bits);
1356-
F32Range(value, value, RangeEnd::Included)
1357-
}
1358-
ty::FloatTy::F64 => {
1359-
let value = rustc_apfloat::ieee::Double::from_bits(bits);
1360-
F64Range(value, value, RangeEnd::Included)
1361-
}
1362-
};
1363-
fields = Fields::empty();
1364-
}
1365-
ty::Ref(_, t, _) if t.is_str() => {
1366-
// We want a `&str` constant to behave like a `Deref` pattern, to be compatible
1367-
// with other `Deref` patterns. This could have been done in `const_to_pat`,
1368-
// but that causes issues with the rest of the matching code.
1369-
// So here, the constructor for a `"foo"` pattern is `&` (represented by
1370-
// `Single`), and has one field. That field has constructor `Str(value)` and no
1371-
// fields.
1372-
// Note: `t` is `str`, not `&str`.
1373-
let subpattern =
1374-
DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span);
1375-
ctor = Single;
1376-
fields = Fields::singleton(cx, subpattern)
1377-
}
1378-
// All constants that can be structurally matched have already been expanded
1379-
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
1380-
// opaque.
1381-
_ => {
1382-
ctor = Opaque;
1383-
fields = Fields::empty();
1384-
}
1347+
match pat.ty.kind() {
1348+
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) => {
1349+
ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) {
1350+
Some(bits) => IntRange(IntRange::from_bits(cx.tcx, pat.ty, bits)),
1351+
None => Opaque,
1352+
};
1353+
fields = Fields::empty();
1354+
}
1355+
ty::Float(ty::FloatTy::F32) => {
1356+
ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) {
1357+
Some(bits) => {
1358+
use rustc_apfloat::Float;
1359+
let value = rustc_apfloat::ieee::Single::from_bits(bits);
1360+
F32Range(value, value, RangeEnd::Included)
1361+
}
1362+
None => Opaque,
1363+
};
1364+
fields = Fields::empty();
1365+
}
1366+
ty::Float(ty::FloatTy::F64) => {
1367+
ctor = match fast_try_eval_bits(cx.tcx, cx.param_env, value) {
1368+
Some(bits) => {
1369+
use rustc_apfloat::Float;
1370+
let value = rustc_apfloat::ieee::Double::from_bits(bits);
1371+
F64Range(value, value, RangeEnd::Included)
1372+
}
1373+
None => Opaque,
1374+
};
1375+
fields = Fields::empty();
1376+
}
1377+
ty::Ref(_, t, _) if t.is_str() => {
1378+
// We want a `&str` constant to behave like a `Deref` pattern, to be compatible
1379+
// with other `Deref` patterns. This could have been done in `const_to_pat`,
1380+
// but that causes issues with the rest of the matching code.
1381+
// So here, the constructor for a `"foo"` pattern is `&` (represented by
1382+
// `Single`), and has one field. That field has constructor `Str(value)` and no
1383+
// fields.
1384+
// Note: `t` is `str`, not `&str`.
1385+
let subpattern =
1386+
DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span);
1387+
ctor = Single;
1388+
fields = Fields::singleton(cx, subpattern)
1389+
}
1390+
// All constants that can be structurally matched have already been expanded
1391+
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
1392+
// opaque.
1393+
_ => {
1394+
ctor = Opaque;
1395+
fields = Fields::empty();
13851396
}
13861397
}
13871398
}
1388-
&PatKind::Range(box PatRange { lo, hi, end }) => {
1399+
PatKind::Range(box PatRange { lo, hi, end }) => {
13891400
use rustc_apfloat::Float;
13901401
let ty = lo.ty();
1391-
let lo = lo.eval_bits(cx.tcx, cx.param_env);
1392-
let hi = hi.eval_bits(cx.tcx, cx.param_env);
1402+
let lo = fast_try_eval_bits(cx.tcx, cx.param_env, lo).unwrap();
1403+
let hi = fast_try_eval_bits(cx.tcx, cx.param_env, hi).unwrap();
13931404
ctor = match ty.kind() {
13941405
ty::Char | ty::Int(_) | ty::Uint(_) => {
1395-
IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, &end).unwrap())
1406+
IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, *end))
13961407
}
13971408
ty::Float(ty::FloatTy::F32) => {
13981409
let lo = rustc_apfloat::ieee::Single::from_bits(lo);

0 commit comments

Comments
 (0)