1 #![allow(clippy::float_cmp)]
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}
;
20 /// A `LitKind`-like enum to fold constant `Expr`s into.
21 #[derive(Debug, Clone)]
23 /// A `String` (e.g., "abc").
25 /// A binary string (e.g., `b"abc"`).
27 /// A single `char` (e.g., `'a'`).
29 /// An integer's bit representation.
35 /// `true` or `false`.
37 /// An array of constants.
39 /// Also an array, but with only one constant, repeated N times.
40 Repeat(Box
<Constant
>, u64),
41 /// A tuple of constants.
47 /// A literal with syntax error.
51 impl PartialEq
for Constant
{
52 fn eq(&self, other
: &Self) -> bool
{
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()
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()
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?
80 impl Hash
for Constant
{
81 fn hash
<H
>(&self, state
: &mut H
)
85 std
::mem
::discriminant(self).hash(state
);
90 Self::Binary(ref b
) => {
100 f64::from(f
).to_bits().hash(state
);
103 f
.to_bits().hash(state
);
108 Self::Vec(ref v
) | Self::Tuple(ref v
) => {
111 Self::Repeat(ref c
, l
) => {
118 Self::Ref(ref r
) => {
121 Self::Err(ref s
) => {
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
)))
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
)),
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?
160 /// Parses a `LitKind` to a `Constant`.
161 pub fn lit_to_constant(lit
: &LitKind
, ty
: Option
<Ty
<'_
>>) -> Constant
{
163 LitKind
::Str(ref is
, _
) => Constant
::Str(is
.to_string()),
164 LitKind
::Byte(b
) => Constant
::Int(u128
::from(b
)),
165 LitKind
::ByteStr(ref s
) => Constant
::Binary(Lrc
::clone(s
)),
166 LitKind
::Char(c
) => Constant
::Char(c
),
167 LitKind
::Int(n
, _
) => Constant
::Int(n
),
168 LitKind
::Float(ref is
, LitFloatType
::Suffixed(fty
)) => match fty
{
169 ast
::FloatTy
::F32
=> Constant
::F32(is
.as_str().parse().unwrap()),
170 ast
::FloatTy
::F64
=> Constant
::F64(is
.as_str().parse().unwrap()),
172 LitKind
::Float(ref is
, LitFloatType
::Unsuffixed
) => match ty
.expect("type of float is known").kind() {
173 ty
::Float(FloatTy
::F32
) => Constant
::F32(is
.as_str().parse().unwrap()),
174 ty
::Float(FloatTy
::F64
) => Constant
::F64(is
.as_str().parse().unwrap()),
177 LitKind
::Bool(b
) => Constant
::Bool(b
),
178 LitKind
::Err(s
) => Constant
::Err(s
),
182 pub fn constant
<'tcx
>(
183 lcx
: &LateContext
<'tcx
>,
184 typeck_results
: &ty
::TypeckResults
<'tcx
>,
186 ) -> Option
<(Constant
, bool
)> {
187 let mut cx
= ConstEvalLateContext
{
190 param_env
: lcx
.param_env
,
191 needed_resolution
: false,
192 substs
: lcx
.tcx
.intern_substs(&[]),
194 cx
.expr(e
).map(|cst
| (cst
, cx
.needed_resolution
))
197 pub fn constant_simple
<'tcx
>(
198 lcx
: &LateContext
<'tcx
>,
199 typeck_results
: &ty
::TypeckResults
<'tcx
>,
201 ) -> Option
<Constant
> {
202 constant(lcx
, typeck_results
, e
).and_then(|(cst
, res
)| if res { None }
else { Some(cst) }
)
205 /// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
206 pub fn constant_context
<'a
, 'tcx
>(
207 lcx
: &'a LateContext
<'tcx
>,
208 typeck_results
: &'a ty
::TypeckResults
<'tcx
>,
209 ) -> ConstEvalLateContext
<'a
, 'tcx
> {
210 ConstEvalLateContext
{
213 param_env
: lcx
.param_env
,
214 needed_resolution
: false,
215 substs
: lcx
.tcx
.intern_substs(&[]),
219 pub struct ConstEvalLateContext
<'a
, 'tcx
> {
220 lcx
: &'a LateContext
<'tcx
>,
221 typeck_results
: &'a ty
::TypeckResults
<'tcx
>,
222 param_env
: ty
::ParamEnv
<'tcx
>,
223 needed_resolution
: bool
,
224 substs
: SubstsRef
<'tcx
>,
227 impl<'a
, 'tcx
> ConstEvalLateContext
<'a
, 'tcx
> {
228 /// Simple constant folding: Insert an expression, get a constant or none.
229 pub fn expr(&mut self, e
: &Expr
<'_
>) -> Option
<Constant
> {
231 ExprKind
::Path(ref qpath
) => self.fetch_path(qpath
, e
.hir_id
, self.typeck_results
.expr_ty(e
)),
232 ExprKind
::Block(block
, _
) => self.block(block
),
233 ExprKind
::Lit(ref lit
) => {
234 if is_direct_expn_of(e
.span
, "cfg").is_some() {
237 Some(lit_to_constant(&lit
.node
, self.typeck_results
.expr_ty_opt(e
)))
240 ExprKind
::Array(vec
) => self.multi(vec
).map(Constant
::Vec
),
241 ExprKind
::Tup(tup
) => self.multi(tup
).map(Constant
::Tuple
),
242 ExprKind
::Repeat(value
, _
) => {
243 let n
= match self.typeck_results
.expr_ty(e
).kind() {
244 ty
::Array(_
, n
) => n
.try_eval_usize(self.lcx
.tcx
, self.lcx
.param_env
)?
,
245 _
=> span_bug
!(e
.span
, "typeck error"),
247 self.expr(value
).map(|v
| Constant
::Repeat(Box
::new(v
), n
))
249 ExprKind
::Unary(op
, operand
) => self.expr(operand
).and_then(|o
| match op
{
250 UnOp
::Not
=> self.constant_not(&o
, self.typeck_results
.expr_ty(e
)),
251 UnOp
::Neg
=> self.constant_negate(&o
, self.typeck_results
.expr_ty(e
)),
252 UnOp
::Deref
=> Some(if let Constant
::Ref(r
) = o { *r }
else { o }
),
254 ExprKind
::If(cond
, then
, ref otherwise
) => self.ifthenelse(cond
, then
, *otherwise
),
255 ExprKind
::Binary(op
, left
, right
) => self.binop(op
, left
, right
),
256 ExprKind
::Call(callee
, args
) => {
257 // We only handle a few const functions for now.
260 if let ExprKind
::Path(qpath
) = &callee
.kind
;
261 let res
= self.typeck_results
.qpath_res(qpath
, callee
.hir_id
);
262 if let Some(def_id
) = res
.opt_def_id();
263 let def_path
: Vec
<_
> = self.lcx
.get_def_path(def_id
).into_iter().map(Symbol
::as_str
).collect();
264 let def_path
: Vec
<&str> = def_path
.iter().take(4).map(|s
| &**s
).collect();
265 if let ["core", "num", int_impl
, "max_value"] = *def_path
;
267 let value
= match int_impl
{
268 "<impl i8>" => i8::MAX
as u128
,
269 "<impl i16>" => i16::MAX
as u128
,
270 "<impl i32>" => i32::MAX
as u128
,
271 "<impl i64>" => i64::MAX
as u128
,
272 "<impl i128>" => i128
::MAX
as u128
,
275 Some(Constant
::Int(value
))
282 ExprKind
::Index(arr
, index
) => self.index(arr
, index
),
283 ExprKind
::AddrOf(_
, _
, inner
) => self.expr(inner
).map(|r
| Constant
::Ref(Box
::new(r
))),
284 // TODO: add other expressions.
289 #[allow(clippy::cast_possible_wrap)]
290 fn constant_not(&self, o
: &Constant
, ty
: Ty
<'_
>) -> Option
<Constant
> {
291 use self::Constant
::{Bool, Int}
;
293 Bool(b
) => Some(Bool(!b
)),
297 ty
::Int(ity
) => Some(Int(unsext(self.lcx
.tcx
, value
as i128
, ity
))),
298 ty
::Uint(ity
) => Some(Int(clip(self.lcx
.tcx
, value
, ity
))),
306 fn constant_negate(&self, o
: &Constant
, ty
: Ty
<'_
>) -> Option
<Constant
> {
307 use self::Constant
::{Int, F32, F64}
;
310 let ity
= match *ty
.kind() {
315 let value
= sext(self.lcx
.tcx
, value
, ity
);
316 let value
= value
.checked_neg()?
;
318 Some(Int(unsext(self.lcx
.tcx
, value
, ity
)))
320 F32(f
) => Some(F32(-f
)),
321 F64(f
) => Some(F64(-f
)),
326 /// Create `Some(Vec![..])` of all constants, unless there is any
327 /// non-constant part.
328 fn multi(&mut self, vec
: &[Expr
<'_
>]) -> Option
<Vec
<Constant
>> {
329 vec
.iter().map(|elem
| self.expr(elem
)).collect
::<Option
<_
>>()
332 /// Lookup a possibly constant expression from a `ExprKind::Path`.
333 fn fetch_path(&mut self, qpath
: &QPath
<'_
>, id
: HirId
, ty
: Ty
<'tcx
>) -> Option
<Constant
> {
334 let res
= self.typeck_results
.qpath_res(qpath
, id
);
336 Res
::Def(DefKind
::Const
| DefKind
::AssocConst
, def_id
) => {
337 let substs
= self.typeck_results
.node_substs(id
);
338 let substs
= if self.substs
.is_empty() {
341 substs
.subst(self.lcx
.tcx
, self.substs
)
350 def
: ty
::WithOptConstParam
::unknown(def_id
),
357 .map(|val
| rustc_middle
::ty
::Const
::from_value(self.lcx
.tcx
, val
, ty
))?
;
358 let result
= miri_to_const(result
);
359 if result
.is_some() {
360 self.needed_resolution
= true;
364 // FIXME: cover all usable cases.
369 fn index(&mut self, lhs
: &'_ Expr
<'_
>, index
: &'_ Expr
<'_
>) -> Option
<Constant
> {
370 let lhs
= self.expr(lhs
);
371 let index
= self.expr(index
);
374 (Some(Constant
::Vec(vec
)), Some(Constant
::Int(index
))) => match vec
.get(index
as usize) {
375 Some(Constant
::F32(x
)) => Some(Constant
::F32(*x
)),
376 Some(Constant
::F64(x
)) => Some(Constant
::F64(*x
)),
379 (Some(Constant
::Vec(vec
)), _
) => {
380 if !vec
.is_empty() && vec
.iter().all(|x
| *x
== vec
[0]) {
382 Some(Constant
::F32(x
)) => Some(Constant
::F32(*x
)),
383 Some(Constant
::F64(x
)) => Some(Constant
::F64(*x
)),
394 /// A block can only yield a constant if it only has one constant expression.
395 fn block(&mut self, block
: &Block
<'_
>) -> Option
<Constant
> {
396 if block
.stmts
.is_empty() {
397 block
.expr
.as_ref().and_then(|b
| self.expr(b
))
403 fn ifthenelse(&mut self, cond
: &Expr
<'_
>, then
: &Expr
<'_
>, otherwise
: Option
<&Expr
<'_
>>) -> Option
<Constant
> {
404 if let Some(Constant
::Bool(b
)) = self.expr(cond
) {
408 otherwise
.as_ref().and_then(|expr
| self.expr(expr
))
415 fn binop(&mut self, op
: BinOp
, left
: &Expr
<'_
>, right
: &Expr
<'_
>) -> Option
<Constant
> {
416 let l
= self.expr(left
)?
;
417 let r
= self.expr(right
);
419 (Constant
::Int(l
), Some(Constant
::Int(r
))) => match *self.typeck_results
.expr_ty_opt(left
)?
.kind() {
421 let l
= sext(self.lcx
.tcx
, l
, ity
);
422 let r
= sext(self.lcx
.tcx
, r
, ity
);
423 let zext
= |n
: i128
| Constant
::Int(unsext(self.lcx
.tcx
, n
, ity
));
425 BinOpKind
::Add
=> l
.checked_add(r
).map(zext
),
426 BinOpKind
::Sub
=> l
.checked_sub(r
).map(zext
),
427 BinOpKind
::Mul
=> l
.checked_mul(r
).map(zext
),
428 BinOpKind
::Div
if r
!= 0 => l
.checked_div(r
).map(zext
),
429 BinOpKind
::Rem
if r
!= 0 => l
.checked_rem(r
).map(zext
),
430 BinOpKind
::Shr
=> l
.checked_shr(r
.try_into().expect("invalid shift")).map(zext
),
431 BinOpKind
::Shl
=> l
.checked_shl(r
.try_into().expect("invalid shift")).map(zext
),
432 BinOpKind
::BitXor
=> Some(zext(l ^ r
)),
433 BinOpKind
::BitOr
=> Some(zext(l
| r
)),
434 BinOpKind
::BitAnd
=> Some(zext(l
& r
)),
435 BinOpKind
::Eq
=> Some(Constant
::Bool(l
== r
)),
436 BinOpKind
::Ne
=> Some(Constant
::Bool(l
!= r
)),
437 BinOpKind
::Lt
=> Some(Constant
::Bool(l
< r
)),
438 BinOpKind
::Le
=> Some(Constant
::Bool(l
<= r
)),
439 BinOpKind
::Ge
=> Some(Constant
::Bool(l
>= r
)),
440 BinOpKind
::Gt
=> Some(Constant
::Bool(l
> r
)),
444 ty
::Uint(_
) => match op
.node
{
445 BinOpKind
::Add
=> l
.checked_add(r
).map(Constant
::Int
),
446 BinOpKind
::Sub
=> l
.checked_sub(r
).map(Constant
::Int
),
447 BinOpKind
::Mul
=> l
.checked_mul(r
).map(Constant
::Int
),
448 BinOpKind
::Div
=> l
.checked_div(r
).map(Constant
::Int
),
449 BinOpKind
::Rem
=> l
.checked_rem(r
).map(Constant
::Int
),
450 BinOpKind
::Shr
=> l
.checked_shr(r
.try_into().expect("shift too large")).map(Constant
::Int
),
451 BinOpKind
::Shl
=> l
.checked_shl(r
.try_into().expect("shift too large")).map(Constant
::Int
),
452 BinOpKind
::BitXor
=> Some(Constant
::Int(l ^ r
)),
453 BinOpKind
::BitOr
=> Some(Constant
::Int(l
| r
)),
454 BinOpKind
::BitAnd
=> Some(Constant
::Int(l
& r
)),
455 BinOpKind
::Eq
=> Some(Constant
::Bool(l
== r
)),
456 BinOpKind
::Ne
=> Some(Constant
::Bool(l
!= r
)),
457 BinOpKind
::Lt
=> Some(Constant
::Bool(l
< r
)),
458 BinOpKind
::Le
=> Some(Constant
::Bool(l
<= r
)),
459 BinOpKind
::Ge
=> Some(Constant
::Bool(l
>= r
)),
460 BinOpKind
::Gt
=> Some(Constant
::Bool(l
> r
)),
465 (Constant
::F32(l
), Some(Constant
::F32(r
))) => match op
.node
{
466 BinOpKind
::Add
=> Some(Constant
::F32(l
+ r
)),
467 BinOpKind
::Sub
=> Some(Constant
::F32(l
- r
)),
468 BinOpKind
::Mul
=> Some(Constant
::F32(l
* r
)),
469 BinOpKind
::Div
=> Some(Constant
::F32(l
/ r
)),
470 BinOpKind
::Rem
=> Some(Constant
::F32(l
% r
)),
471 BinOpKind
::Eq
=> Some(Constant
::Bool(l
== r
)),
472 BinOpKind
::Ne
=> Some(Constant
::Bool(l
!= r
)),
473 BinOpKind
::Lt
=> Some(Constant
::Bool(l
< r
)),
474 BinOpKind
::Le
=> Some(Constant
::Bool(l
<= r
)),
475 BinOpKind
::Ge
=> Some(Constant
::Bool(l
>= r
)),
476 BinOpKind
::Gt
=> Some(Constant
::Bool(l
> r
)),
479 (Constant
::F64(l
), Some(Constant
::F64(r
))) => match op
.node
{
480 BinOpKind
::Add
=> Some(Constant
::F64(l
+ r
)),
481 BinOpKind
::Sub
=> Some(Constant
::F64(l
- r
)),
482 BinOpKind
::Mul
=> Some(Constant
::F64(l
* r
)),
483 BinOpKind
::Div
=> Some(Constant
::F64(l
/ r
)),
484 BinOpKind
::Rem
=> Some(Constant
::F64(l
% r
)),
485 BinOpKind
::Eq
=> Some(Constant
::Bool(l
== r
)),
486 BinOpKind
::Ne
=> Some(Constant
::Bool(l
!= r
)),
487 BinOpKind
::Lt
=> Some(Constant
::Bool(l
< r
)),
488 BinOpKind
::Le
=> Some(Constant
::Bool(l
<= r
)),
489 BinOpKind
::Ge
=> Some(Constant
::Bool(l
>= r
)),
490 BinOpKind
::Gt
=> Some(Constant
::Bool(l
> r
)),
493 (l
, r
) => match (op
.node
, l
, r
) {
494 (BinOpKind
::And
, Constant
::Bool(false), _
) => Some(Constant
::Bool(false)),
495 (BinOpKind
::Or
, Constant
::Bool(true), _
) => Some(Constant
::Bool(true)),
496 (BinOpKind
::And
, Constant
::Bool(true), Some(r
)) | (BinOpKind
::Or
, Constant
::Bool(false), Some(r
)) => {
499 (BinOpKind
::BitXor
, Constant
::Bool(l
), Some(Constant
::Bool(r
))) => Some(Constant
::Bool(l ^ r
)),
500 (BinOpKind
::BitAnd
, Constant
::Bool(l
), Some(Constant
::Bool(r
))) => Some(Constant
::Bool(l
& r
)),
501 (BinOpKind
::BitOr
, Constant
::Bool(l
), Some(Constant
::Bool(r
))) => Some(Constant
::Bool(l
| r
)),
508 pub fn miri_to_const(result
: &ty
::Const
<'_
>) -> Option
<Constant
> {
509 use rustc_middle
::mir
::interpret
::ConstValue
;
511 ty
::ConstKind
::Value(ConstValue
::Scalar(Scalar
::Int(int
))) => {
512 match result
.ty
.kind() {
513 ty
::Bool
=> Some(Constant
::Bool(int
== ScalarInt
::TRUE
)),
514 ty
::Uint(_
) | ty
::Int(_
) => Some(Constant
::Int(int
.assert_bits(int
.size()))),
515 ty
::Float(FloatTy
::F32
) => Some(Constant
::F32(f32::from_bits(
516 int
.try_into().expect("invalid f32 bit representation"),
518 ty
::Float(FloatTy
::F64
) => Some(Constant
::F64(f64::from_bits(
519 int
.try_into().expect("invalid f64 bit representation"),
521 ty
::RawPtr(type_and_mut
) => {
522 if let ty
::Uint(_
) = type_and_mut
.ty
.kind() {
523 return Some(Constant
::RawPtr(int
.assert_bits(int
.size())));
527 // FIXME: implement other conversions.
531 ty
::ConstKind
::Value(ConstValue
::Slice { data, start, end }
) => match result
.ty
.kind() {
532 ty
::Ref(_
, tam
, _
) => match tam
.kind() {
533 ty
::Str
=> String
::from_utf8(
534 data
.inspect_with_uninit_and_ptr_outside_interpreter(start
..end
)
543 ty
::ConstKind
::Value(ConstValue
::ByRef { alloc, offset: _ }
) => match result
.ty
.kind() {
544 ty
::Array(sub_type
, len
) => match sub_type
.kind() {
545 ty
::Float(FloatTy
::F32
) => match miri_to_const(len
) {
546 Some(Constant
::Int(len
)) => alloc
547 .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len
as usize))
551 Some(Constant
::F32(f32::from_le_bytes(
552 chunk
.try_into().expect("this shouldn't happen"),
555 .collect
::<Option
<Vec
<Constant
>>>()
559 ty
::Float(FloatTy
::F64
) => match miri_to_const(len
) {
560 Some(Constant
::Int(len
)) => alloc
561 .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len
as usize))
565 Some(Constant
::F64(f64::from_le_bytes(
566 chunk
.try_into().expect("this shouldn't happen"),
569 .collect
::<Option
<Vec
<Constant
>>>()
573 // FIXME: implement other array type conversions.
578 // FIXME: implement other conversions.