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