]> git.lizzy.rs Git - rust.git/blob - clippy_utils/src/consts.rs
Merge commit '57b3c4b90f4346b3990c1be387c3b3ca7b78412c' into clippyup
[rust.git] / clippy_utils / src / consts.rs
1 #![allow(clippy::float_cmp)]
2
3 use crate::{clip, is_direct_expn_of, sext, unsext};
4 use if_chain::if_chain;
5 use rustc_ast::ast::{self, LitFloatType, LitKind};
6 use rustc_data_structures::sync::Lrc;
7 use rustc_hir::def::{DefKind, Res};
8 use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
9 use rustc_lint::LateContext;
10 use rustc_middle::mir::interpret::Scalar;
11 use rustc_middle::ty::subst::{Subst, SubstsRef};
12 use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
13 use rustc_middle::{bug, span_bug};
14 use rustc_span::symbol::Symbol;
15 use std::cmp::Ordering::{self, Equal};
16 use std::convert::TryInto;
17 use std::hash::{Hash, Hasher};
18 use std::iter;
19
20 /// A `LitKind`-like enum to fold constant `Expr`s into.
21 #[derive(Debug, Clone)]
22 pub enum Constant {
23     /// A `String` (e.g., "abc").
24     Str(String),
25     /// A binary string (e.g., `b"abc"`).
26     Binary(Lrc<[u8]>),
27     /// A single `char` (e.g., `'a'`).
28     Char(char),
29     /// An integer's bit representation.
30     Int(u128),
31     /// An `f32`.
32     F32(f32),
33     /// An `f64`.
34     F64(f64),
35     /// `true` or `false`.
36     Bool(bool),
37     /// An array of constants.
38     Vec(Vec<Constant>),
39     /// Also an array, but with only one constant, repeated N times.
40     Repeat(Box<Constant>, u64),
41     /// A tuple of constants.
42     Tuple(Vec<Constant>),
43     /// A raw pointer.
44     RawPtr(u128),
45     /// A reference
46     Ref(Box<Constant>),
47     /// A literal with syntax error.
48     Err(Symbol),
49 }
50
51 impl PartialEq for Constant {
52     fn eq(&self, other: &Self) -> bool {
53         match (self, other) {
54             (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
55             (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
56             (&Self::Char(l), &Self::Char(r)) => l == r,
57             (&Self::Int(l), &Self::Int(r)) => l == r,
58             (&Self::F64(l), &Self::F64(r)) => {
59                 // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
60                 // `Fw32 == Fw64`, so don’t compare them.
61                 // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
62                 l.to_bits() == r.to_bits()
63             },
64             (&Self::F32(l), &Self::F32(r)) => {
65                 // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
66                 // `Fw32 == Fw64`, so don’t compare them.
67                 // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
68                 f64::from(l).to_bits() == f64::from(r).to_bits()
69             },
70             (&Self::Bool(l), &Self::Bool(r)) => l == r,
71             (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
72             (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
73             (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
74             // TODO: are there inter-type equalities?
75             _ => false,
76         }
77     }
78 }
79
80 impl Hash for Constant {
81     fn hash<H>(&self, state: &mut H)
82     where
83         H: Hasher,
84     {
85         std::mem::discriminant(self).hash(state);
86         match *self {
87             Self::Str(ref s) => {
88                 s.hash(state);
89             },
90             Self::Binary(ref b) => {
91                 b.hash(state);
92             },
93             Self::Char(c) => {
94                 c.hash(state);
95             },
96             Self::Int(i) => {
97                 i.hash(state);
98             },
99             Self::F32(f) => {
100                 f64::from(f).to_bits().hash(state);
101             },
102             Self::F64(f) => {
103                 f.to_bits().hash(state);
104             },
105             Self::Bool(b) => {
106                 b.hash(state);
107             },
108             Self::Vec(ref v) | Self::Tuple(ref v) => {
109                 v.hash(state);
110             },
111             Self::Repeat(ref c, l) => {
112                 c.hash(state);
113                 l.hash(state);
114             },
115             Self::RawPtr(u) => {
116                 u.hash(state);
117             },
118             Self::Ref(ref r) => {
119                 r.hash(state);
120             },
121             Self::Err(ref s) => {
122                 s.hash(state);
123             },
124         }
125     }
126 }
127
128 impl Constant {
129     pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
130         match (left, right) {
131             (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
132             (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
133             (&Self::Int(l), &Self::Int(r)) => {
134                 if let ty::Int(int_ty) = *cmp_type.kind() {
135                     Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
136                 } else {
137                     Some(l.cmp(&r))
138                 }
139             },
140             (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
141             (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
142             (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
143             (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => iter::zip(l, r)
144                 .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
145                 .find(|r| r.map_or(true, |o| o != Ordering::Equal))
146                 .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
147             (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
148                 match Self::partial_cmp(tcx, cmp_type, lv, rv) {
149                     Some(Equal) => Some(ls.cmp(rs)),
150                     x => x,
151                 }
152             },
153             (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb),
154             // TODO: are there any useful inter-type orderings?
155             _ => None,
156         }
157     }
158
159     /// Returns the integer value or `None` if `self` or `val_type` is not integer type.
160     pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> {
161         if let Constant::Int(const_int) = *self {
162             match *val_type.kind() {
163                 ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
164                 ty::Uint(_) => Some(FullInt::U(const_int)),
165                 _ => None,
166             }
167         } else {
168             None
169         }
170     }
171
172     #[must_use]
173     pub fn peel_refs(mut self) -> Self {
174         while let Constant::Ref(r) = self {
175             self = *r;
176         }
177         self
178     }
179 }
180
181 /// Parses a `LitKind` to a `Constant`.
182 pub fn lit_to_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
183     match *lit {
184         LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
185         LitKind::Byte(b) => Constant::Int(u128::from(b)),
186         LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
187         LitKind::Char(c) => Constant::Char(c),
188         LitKind::Int(n, _) => Constant::Int(n),
189         LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
190             ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
191             ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
192         },
193         LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
194             ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
195             ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
196             _ => bug!(),
197         },
198         LitKind::Bool(b) => Constant::Bool(b),
199         LitKind::Err(s) => Constant::Err(s),
200     }
201 }
202
203 pub fn constant<'tcx>(
204     lcx: &LateContext<'tcx>,
205     typeck_results: &ty::TypeckResults<'tcx>,
206     e: &Expr<'_>,
207 ) -> Option<(Constant, bool)> {
208     let mut cx = ConstEvalLateContext {
209         lcx,
210         typeck_results,
211         param_env: lcx.param_env,
212         needed_resolution: false,
213         substs: lcx.tcx.intern_substs(&[]),
214     };
215     cx.expr(e).map(|cst| (cst, cx.needed_resolution))
216 }
217
218 pub fn constant_simple<'tcx>(
219     lcx: &LateContext<'tcx>,
220     typeck_results: &ty::TypeckResults<'tcx>,
221     e: &Expr<'_>,
222 ) -> Option<Constant> {
223     constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
224 }
225
226 pub fn constant_full_int<'tcx>(
227     lcx: &LateContext<'tcx>,
228     typeck_results: &ty::TypeckResults<'tcx>,
229     e: &Expr<'_>,
230 ) -> Option<FullInt> {
231     constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
232 }
233
234 #[derive(Copy, Clone, Debug, Eq)]
235 pub enum FullInt {
236     S(i128),
237     U(u128),
238 }
239
240 impl PartialEq for FullInt {
241     #[must_use]
242     fn eq(&self, other: &Self) -> bool {
243         self.cmp(other) == Ordering::Equal
244     }
245 }
246
247 impl PartialOrd for FullInt {
248     #[must_use]
249     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
250         Some(self.cmp(other))
251     }
252 }
253
254 impl Ord for FullInt {
255     #[must_use]
256     fn cmp(&self, other: &Self) -> Ordering {
257         use FullInt::{S, U};
258
259         fn cmp_s_u(s: i128, u: u128) -> Ordering {
260             u128::try_from(s).map_or(Ordering::Less, |x| x.cmp(&u))
261         }
262
263         match (*self, *other) {
264             (S(s), S(o)) => s.cmp(&o),
265             (U(s), U(o)) => s.cmp(&o),
266             (S(s), U(o)) => cmp_s_u(s, o),
267             (U(s), S(o)) => cmp_s_u(o, s).reverse(),
268         }
269     }
270 }
271
272 /// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
273 pub fn constant_context<'a, 'tcx>(
274     lcx: &'a LateContext<'tcx>,
275     typeck_results: &'a ty::TypeckResults<'tcx>,
276 ) -> ConstEvalLateContext<'a, 'tcx> {
277     ConstEvalLateContext {
278         lcx,
279         typeck_results,
280         param_env: lcx.param_env,
281         needed_resolution: false,
282         substs: lcx.tcx.intern_substs(&[]),
283     }
284 }
285
286 pub struct ConstEvalLateContext<'a, 'tcx> {
287     lcx: &'a LateContext<'tcx>,
288     typeck_results: &'a ty::TypeckResults<'tcx>,
289     param_env: ty::ParamEnv<'tcx>,
290     needed_resolution: bool,
291     substs: SubstsRef<'tcx>,
292 }
293
294 impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
295     /// Simple constant folding: Insert an expression, get a constant or none.
296     pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
297         match e.kind {
298             ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
299             ExprKind::Block(block, _) => self.block(block),
300             ExprKind::Lit(ref lit) => {
301                 if is_direct_expn_of(e.span, "cfg").is_some() {
302                     None
303                 } else {
304                     Some(lit_to_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
305                 }
306             },
307             ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
308             ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
309             ExprKind::Repeat(value, _) => {
310                 let n = match self.typeck_results.expr_ty(e).kind() {
311                     ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
312                     _ => span_bug!(e.span, "typeck error"),
313                 };
314                 self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
315             },
316             ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
317                 UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
318                 UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
319                 UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
320             }),
321             ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
322             ExprKind::Binary(op, left, right) => self.binop(op, left, right),
323             ExprKind::Call(callee, args) => {
324                 // We only handle a few const functions for now.
325                 if_chain! {
326                     if args.is_empty();
327                     if let ExprKind::Path(qpath) = &callee.kind;
328                     let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
329                     if let Some(def_id) = res.opt_def_id();
330                     let def_path = self.lcx.get_def_path(def_id);
331                     let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect();
332                     if let ["core", "num", int_impl, "max_value"] = *def_path;
333                     then {
334                         let value = match int_impl {
335                             "<impl i8>" => i8::MAX as u128,
336                             "<impl i16>" => i16::MAX as u128,
337                             "<impl i32>" => i32::MAX as u128,
338                             "<impl i64>" => i64::MAX as u128,
339                             "<impl i128>" => i128::MAX as u128,
340                             _ => return None,
341                         };
342                         Some(Constant::Int(value))
343                     } else {
344                         None
345                     }
346                 }
347             },
348             ExprKind::Index(arr, index) => self.index(arr, index),
349             ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
350             // TODO: add other expressions.
351             _ => None,
352         }
353     }
354
355     #[allow(clippy::cast_possible_wrap)]
356     fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
357         use self::Constant::{Bool, Int};
358         match *o {
359             Bool(b) => Some(Bool(!b)),
360             Int(value) => {
361                 let value = !value;
362                 match *ty.kind() {
363                     ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
364                     ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
365                     _ => None,
366                 }
367             },
368             _ => None,
369         }
370     }
371
372     fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
373         use self::Constant::{Int, F32, F64};
374         match *o {
375             Int(value) => {
376                 let ity = match *ty.kind() {
377                     ty::Int(ity) => ity,
378                     _ => return None,
379                 };
380                 // sign extend
381                 let value = sext(self.lcx.tcx, value, ity);
382                 let value = value.checked_neg()?;
383                 // clear unused bits
384                 Some(Int(unsext(self.lcx.tcx, value, ity)))
385             },
386             F32(f) => Some(F32(-f)),
387             F64(f) => Some(F64(-f)),
388             _ => None,
389         }
390     }
391
392     /// Create `Some(Vec![..])` of all constants, unless there is any
393     /// non-constant part.
394     fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
395         vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
396     }
397
398     /// Lookup a possibly constant expression from an `ExprKind::Path`.
399     fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
400         let res = self.typeck_results.qpath_res(qpath, id);
401         match res {
402             Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
403                 let substs = self.typeck_results.node_substs(id);
404                 let substs = if self.substs.is_empty() {
405                     substs
406                 } else {
407                     substs.subst(self.lcx.tcx, self.substs)
408                 };
409
410                 let result = self
411                     .lcx
412                     .tcx
413                     .const_eval_resolve(
414                         self.param_env,
415                         ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
416                         None,
417                     )
418                     .ok()
419                     .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
420                 let result = miri_to_const(result);
421                 if result.is_some() {
422                     self.needed_resolution = true;
423                 }
424                 result
425             },
426             // FIXME: cover all usable cases.
427             _ => None,
428         }
429     }
430
431     fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
432         let lhs = self.expr(lhs);
433         let index = self.expr(index);
434
435         match (lhs, index) {
436             (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
437                 Some(Constant::F32(x)) => Some(Constant::F32(*x)),
438                 Some(Constant::F64(x)) => Some(Constant::F64(*x)),
439                 _ => None,
440             },
441             (Some(Constant::Vec(vec)), _) => {
442                 if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
443                     match vec.get(0) {
444                         Some(Constant::F32(x)) => Some(Constant::F32(*x)),
445                         Some(Constant::F64(x)) => Some(Constant::F64(*x)),
446                         _ => None,
447                     }
448                 } else {
449                     None
450                 }
451             },
452             _ => None,
453         }
454     }
455
456     /// A block can only yield a constant if it only has one constant expression.
457     fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
458         if block.stmts.is_empty() {
459             block.expr.as_ref().and_then(|b| self.expr(b))
460         } else {
461             None
462         }
463     }
464
465     fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
466         if let Some(Constant::Bool(b)) = self.expr(cond) {
467             if b {
468                 self.expr(&*then)
469             } else {
470                 otherwise.as_ref().and_then(|expr| self.expr(expr))
471             }
472         } else {
473             None
474         }
475     }
476
477     fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
478         let l = self.expr(left)?;
479         let r = self.expr(right);
480         match (l, r) {
481             (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
482                 ty::Int(ity) => {
483                     let l = sext(self.lcx.tcx, l, ity);
484                     let r = sext(self.lcx.tcx, r, ity);
485                     let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
486                     match op.node {
487                         BinOpKind::Add => l.checked_add(r).map(zext),
488                         BinOpKind::Sub => l.checked_sub(r).map(zext),
489                         BinOpKind::Mul => l.checked_mul(r).map(zext),
490                         BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
491                         BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
492                         BinOpKind::Shr => l.checked_shr(r.try_into().expect("invalid shift")).map(zext),
493                         BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).map(zext),
494                         BinOpKind::BitXor => Some(zext(l ^ r)),
495                         BinOpKind::BitOr => Some(zext(l | r)),
496                         BinOpKind::BitAnd => Some(zext(l & r)),
497                         BinOpKind::Eq => Some(Constant::Bool(l == r)),
498                         BinOpKind::Ne => Some(Constant::Bool(l != r)),
499                         BinOpKind::Lt => Some(Constant::Bool(l < r)),
500                         BinOpKind::Le => Some(Constant::Bool(l <= r)),
501                         BinOpKind::Ge => Some(Constant::Bool(l >= r)),
502                         BinOpKind::Gt => Some(Constant::Bool(l > r)),
503                         _ => None,
504                     }
505                 },
506                 ty::Uint(_) => match op.node {
507                     BinOpKind::Add => l.checked_add(r).map(Constant::Int),
508                     BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
509                     BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
510                     BinOpKind::Div => l.checked_div(r).map(Constant::Int),
511                     BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
512                     BinOpKind::Shr => l.checked_shr(r.try_into().expect("shift too large")).map(Constant::Int),
513                     BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).map(Constant::Int),
514                     BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
515                     BinOpKind::BitOr => Some(Constant::Int(l | r)),
516                     BinOpKind::BitAnd => Some(Constant::Int(l & r)),
517                     BinOpKind::Eq => Some(Constant::Bool(l == r)),
518                     BinOpKind::Ne => Some(Constant::Bool(l != r)),
519                     BinOpKind::Lt => Some(Constant::Bool(l < r)),
520                     BinOpKind::Le => Some(Constant::Bool(l <= r)),
521                     BinOpKind::Ge => Some(Constant::Bool(l >= r)),
522                     BinOpKind::Gt => Some(Constant::Bool(l > r)),
523                     _ => None,
524                 },
525                 _ => None,
526             },
527             (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
528                 BinOpKind::Add => Some(Constant::F32(l + r)),
529                 BinOpKind::Sub => Some(Constant::F32(l - r)),
530                 BinOpKind::Mul => Some(Constant::F32(l * r)),
531                 BinOpKind::Div => Some(Constant::F32(l / r)),
532                 BinOpKind::Rem => Some(Constant::F32(l % r)),
533                 BinOpKind::Eq => Some(Constant::Bool(l == r)),
534                 BinOpKind::Ne => Some(Constant::Bool(l != r)),
535                 BinOpKind::Lt => Some(Constant::Bool(l < r)),
536                 BinOpKind::Le => Some(Constant::Bool(l <= r)),
537                 BinOpKind::Ge => Some(Constant::Bool(l >= r)),
538                 BinOpKind::Gt => Some(Constant::Bool(l > r)),
539                 _ => None,
540             },
541             (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
542                 BinOpKind::Add => Some(Constant::F64(l + r)),
543                 BinOpKind::Sub => Some(Constant::F64(l - r)),
544                 BinOpKind::Mul => Some(Constant::F64(l * r)),
545                 BinOpKind::Div => Some(Constant::F64(l / r)),
546                 BinOpKind::Rem => Some(Constant::F64(l % r)),
547                 BinOpKind::Eq => Some(Constant::Bool(l == r)),
548                 BinOpKind::Ne => Some(Constant::Bool(l != r)),
549                 BinOpKind::Lt => Some(Constant::Bool(l < r)),
550                 BinOpKind::Le => Some(Constant::Bool(l <= r)),
551                 BinOpKind::Ge => Some(Constant::Bool(l >= r)),
552                 BinOpKind::Gt => Some(Constant::Bool(l > r)),
553                 _ => None,
554             },
555             (l, r) => match (op.node, l, r) {
556                 (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
557                 (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
558                 (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
559                     Some(r)
560                 },
561                 (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
562                 (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
563                 (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
564                 _ => None,
565             },
566         }
567     }
568 }
569
570 pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
571     use rustc_middle::mir::interpret::ConstValue;
572     match result.val {
573         ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
574             match result.ty.kind() {
575                 ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
576                 ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
577                 ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
578                     int.try_into().expect("invalid f32 bit representation"),
579                 ))),
580                 ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
581                     int.try_into().expect("invalid f64 bit representation"),
582                 ))),
583                 ty::RawPtr(type_and_mut) => {
584                     if let ty::Uint(_) = type_and_mut.ty.kind() {
585                         return Some(Constant::RawPtr(int.assert_bits(int.size())));
586                     }
587                     None
588                 },
589                 // FIXME: implement other conversions.
590                 _ => None,
591             }
592         },
593         ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
594             ty::Ref(_, tam, _) => match tam.kind() {
595                 ty::Str => String::from_utf8(
596                     data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
597                         .to_owned(),
598                 )
599                 .ok()
600                 .map(Constant::Str),
601                 _ => None,
602             },
603             _ => None,
604         },
605         ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
606             ty::Array(sub_type, len) => match sub_type.kind() {
607                 ty::Float(FloatTy::F32) => match miri_to_const(len) {
608                     Some(Constant::Int(len)) => alloc
609                         .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
610                         .to_owned()
611                         .chunks(4)
612                         .map(|chunk| {
613                             Some(Constant::F32(f32::from_le_bytes(
614                                 chunk.try_into().expect("this shouldn't happen"),
615                             )))
616                         })
617                         .collect::<Option<Vec<Constant>>>()
618                         .map(Constant::Vec),
619                     _ => None,
620                 },
621                 ty::Float(FloatTy::F64) => match miri_to_const(len) {
622                     Some(Constant::Int(len)) => alloc
623                         .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
624                         .to_owned()
625                         .chunks(8)
626                         .map(|chunk| {
627                             Some(Constant::F64(f64::from_le_bytes(
628                                 chunk.try_into().expect("this shouldn't happen"),
629                             )))
630                         })
631                         .collect::<Option<Vec<Constant>>>()
632                         .map(Constant::Vec),
633                     _ => None,
634                 },
635                 // FIXME: implement other array type conversions.
636                 _ => None,
637             },
638             _ => None,
639         },
640         // FIXME: implement other conversions.
641         _ => None,
642     }
643 }