]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/const_eval.rs
Address behaviour changing review comments
[rust.git] / src / librustc_mir / interpret / const_eval.rs
1 use std::fmt;
2 use std::error::Error;
3
4 use rustc::hir;
5 use rustc::mir::interpret::{ConstEvalErr, ScalarMaybeUndef};
6 use rustc::mir;
7 use rustc::ty::{self, TyCtxt, Ty, Instance};
8 use rustc::ty::layout::{self, LayoutOf, Primitive, TyLayout, Size};
9 use rustc::ty::subst::Subst;
10 use rustc_data_structures::indexed_vec::IndexVec;
11
12 use syntax::ast::Mutability;
13 use syntax::codemap::Span;
14 use syntax::codemap::DUMMY_SP;
15
16 use rustc::mir::interpret::{
17     EvalResult, EvalError, EvalErrorKind, GlobalId,
18     Value, Scalar, AllocId, Allocation, ConstValue,
19 };
20 use super::{Place, EvalContext, StackPopCleanup, ValTy, Memory, MemoryKind};
21
22 pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
23     tcx: TyCtxt<'a, 'tcx, 'tcx>,
24     instance: Instance<'tcx>,
25     mir: &'mir mir::Mir<'tcx>,
26     span: Span,
27 ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>> {
28     debug!("mk_borrowck_eval_cx: {:?}", instance);
29     let param_env = tcx.param_env(instance.def_id());
30     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
31     // insert a stack frame so any queries have the correct substs
32     ecx.stack.push(super::eval_context::Frame {
33         block: mir::START_BLOCK,
34         locals: IndexVec::new(),
35         instance,
36         span,
37         mir,
38         return_place: Place::undef(),
39         return_to_block: StackPopCleanup::None,
40         stmt: 0,
41     });
42     Ok(ecx)
43 }
44
45 pub fn mk_eval_cx<'a, 'tcx>(
46     tcx: TyCtxt<'a, 'tcx, 'tcx>,
47     instance: Instance<'tcx>,
48     param_env: ty::ParamEnv<'tcx>,
49 ) -> EvalResult<'tcx, EvalContext<'a, 'tcx, 'tcx, CompileTimeEvaluator>> {
50     debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
51     let span = tcx.def_span(instance.def_id());
52     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
53     let mir = ecx.load_mir(instance.def)?;
54     // insert a stack frame so any queries have the correct substs
55     ecx.push_stack_frame(
56         instance,
57         mir.span,
58         mir,
59         Place::undef(),
60         StackPopCleanup::None,
61     )?;
62     Ok(ecx)
63 }
64
65 pub fn eval_promoted<'a, 'mir, 'tcx>(
66     ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
67     cid: GlobalId<'tcx>,
68     mir: &'mir mir::Mir<'tcx>,
69     param_env: ty::ParamEnv<'tcx>,
70 ) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
71     ecx.with_fresh_body(|ecx| {
72         eval_body_using_ecx(ecx, cid, Some(mir), param_env)
73     })
74 }
75
76 pub fn value_to_const_value<'tcx>(
77     ecx: &EvalContext<'_, '_, 'tcx, CompileTimeEvaluator>,
78     val: Value,
79     layout: TyLayout<'tcx>,
80 ) -> &'tcx ty::Const<'tcx> {
81     match (val, &layout.abi) {
82         (Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { size: 0, ..})), _) if layout.is_zst() => {},
83         (Value::ByRef(..), _) |
84         (Value::Scalar(_), &layout::Abi::Scalar(_)) |
85         (Value::ScalarPair(..), &layout::Abi::ScalarPair(..)) => {},
86         _ => bug!("bad value/layout combo: {:#?}, {:#?}", val, layout),
87     }
88     let val = (|| {
89         match val {
90             Value::Scalar(val) => Ok(ConstValue::Scalar(val.unwrap_or_err()?)),
91             Value::ScalarPair(a, b) => Ok(ConstValue::ScalarPair(a.unwrap_or_err()?, b.unwrap_or_err()?)),
92             Value::ByRef(ptr, align) => {
93                 let ptr = ptr.to_ptr().unwrap();
94                 let alloc = ecx.memory.get(ptr.alloc_id)?;
95                 assert!(alloc.align.abi() >= align.abi());
96                 assert!(alloc.bytes.len() as u64 - ptr.offset.bytes() >= layout.size.bytes());
97                 let mut alloc = alloc.clone();
98                 alloc.align = align;
99                 let alloc = ecx.tcx.intern_const_alloc(alloc);
100                 Ok(ConstValue::ByRef(alloc, ptr.offset))
101             }
102         }
103     })();
104     match val {
105         Ok(val) => ty::Const::from_const_value(ecx.tcx.tcx, val, layout.ty),
106         Err(error) => {
107             let (stacktrace, span) = ecx.generate_stacktrace(None);
108             let err = ConstEvalErr { span, error, stacktrace };
109             if let Some(mut err) = err.struct_error(ecx.tcx, "failed to convert Value to ConstValue") {
110                 err.delay_as_bug();
111             } else {
112                 span_bug!(span, "failed to convert Value to ConstValue")
113             }
114             let alloc = Allocation::undef(layout.size, layout.align);
115             let alloc = ecx.tcx.intern_const_alloc(alloc);
116             let val = ConstValue::ByRef(alloc, Size::ZERO);
117             ty::Const::from_const_value(ecx.tcx.tcx, val, layout.ty)
118         }
119     }
120 }
121
122 fn eval_body_and_ecx<'a, 'mir, 'tcx>(
123     tcx: TyCtxt<'a, 'tcx, 'tcx>,
124     cid: GlobalId<'tcx>,
125     mir: Option<&'mir mir::Mir<'tcx>>,
126     param_env: ty::ParamEnv<'tcx>,
127 ) -> (EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)>, EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>) {
128     debug!("eval_body_and_ecx: {:?}, {:?}", cid, param_env);
129     // we start out with the best span we have
130     // and try improving it down the road when more information is available
131     let span = tcx.def_span(cid.instance.def_id());
132     let span = mir.map(|mir| mir.span).unwrap_or(span);
133     let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeEvaluator, ());
134     let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
135     (r, ecx)
136 }
137
138 fn eval_body_using_ecx<'a, 'mir, 'tcx>(
139     ecx: &mut EvalContext<'a, 'mir, 'tcx, CompileTimeEvaluator>,
140     cid: GlobalId<'tcx>,
141     mir: Option<&'mir mir::Mir<'tcx>>,
142     param_env: ty::ParamEnv<'tcx>,
143 ) -> EvalResult<'tcx, (Value, Scalar, TyLayout<'tcx>)> {
144     debug!("eval_body: {:?}, {:?}", cid, param_env);
145     let tcx = ecx.tcx.tcx;
146     let mut mir = match mir {
147         Some(mir) => mir,
148         None => ecx.load_mir(cid.instance.def)?,
149     };
150     if let Some(index) = cid.promoted {
151         mir = &mir.promoted[index];
152     }
153     let layout = ecx.layout_of(mir.return_ty().subst(tcx, cid.instance.substs))?;
154     assert!(!layout.is_unsized());
155     let ptr = ecx.memory.allocate(
156         layout.size,
157         layout.align,
158         MemoryKind::Stack,
159     )?;
160     let internally_mutable = !layout.ty.is_freeze(tcx, param_env, mir.span);
161     let is_static = tcx.is_static(cid.instance.def_id());
162     let mutability = if is_static == Some(hir::Mutability::MutMutable) || internally_mutable {
163         Mutability::Mutable
164     } else {
165         Mutability::Immutable
166     };
167     let cleanup = StackPopCleanup::MarkStatic(mutability);
168     let name = ty::tls::with(|tcx| tcx.item_path_str(cid.instance.def_id()));
169     let prom = cid.promoted.map_or(String::new(), |p| format!("::promoted[{:?}]", p));
170     trace!("const_eval: pushing stack frame for global: {}{}", name, prom);
171     assert!(mir.arg_count == 0);
172     ecx.push_stack_frame(
173         cid.instance,
174         mir.span,
175         mir,
176         Place::from_ptr(ptr, layout.align),
177         cleanup,
178     )?;
179
180     while ecx.step()? {}
181     let ptr = ptr.into();
182     // always try to read the value and report errors
183     let value = match ecx.try_read_value(ptr, layout.align, layout.ty)? {
184         Some(val) if is_static.is_none() && cid.promoted.is_none() => val,
185         // point at the allocation
186         _ => Value::ByRef(ptr, layout.align),
187     };
188     Ok((value, ptr, layout))
189 }
190
191 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
192 pub struct CompileTimeEvaluator;
193
194 impl<'tcx> Into<EvalError<'tcx>> for ConstEvalError {
195     fn into(self) -> EvalError<'tcx> {
196         EvalErrorKind::MachineError(self.to_string()).into()
197     }
198 }
199
200 #[derive(Clone, Debug)]
201 enum ConstEvalError {
202     NeedsRfc(String),
203     NotConst(String),
204 }
205
206 impl fmt::Display for ConstEvalError {
207     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
208         use self::ConstEvalError::*;
209         match *self {
210             NeedsRfc(ref msg) => {
211                 write!(
212                     f,
213                     "\"{}\" needs an rfc before being allowed inside constants",
214                     msg
215                 )
216             }
217             NotConst(ref msg) => write!(f, "{}", msg),
218         }
219     }
220 }
221
222 impl Error for ConstEvalError {
223     fn description(&self) -> &str {
224         use self::ConstEvalError::*;
225         match *self {
226             NeedsRfc(_) => "this feature needs an rfc before being allowed inside constants",
227             NotConst(_) => "this feature is not compatible with constant evaluation",
228         }
229     }
230
231     fn cause(&self) -> Option<&dyn Error> {
232         None
233     }
234 }
235
236 impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
237     type MemoryData = ();
238     type MemoryKinds = !;
239     fn eval_fn_call<'a>(
240         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
241         instance: ty::Instance<'tcx>,
242         destination: Option<(Place, mir::BasicBlock)>,
243         args: &[ValTy<'tcx>],
244         span: Span,
245         sig: ty::FnSig<'tcx>,
246     ) -> EvalResult<'tcx, bool> {
247         debug!("eval_fn_call: {:?}", instance);
248         if !ecx.tcx.is_const_fn(instance.def_id()) {
249             let def_id = instance.def_id();
250             let (op, oflo) = if let Some(op) = ecx.tcx.is_binop_lang_item(def_id) {
251                 op
252             } else {
253                 return Err(
254                     ConstEvalError::NotConst(format!("calling non-const fn `{}`", instance)).into(),
255                 );
256             };
257             let (dest, bb) = destination.expect("128 lowerings can't diverge");
258             let dest_ty = sig.output();
259             if oflo {
260                 ecx.intrinsic_with_overflow(op, args[0], args[1], dest, dest_ty)?;
261             } else {
262                 ecx.intrinsic_overflowing(op, args[0], args[1], dest, dest_ty)?;
263             }
264             ecx.goto_block(bb);
265             return Ok(true);
266         }
267         let mir = match ecx.load_mir(instance.def) {
268             Ok(mir) => mir,
269             Err(err) => {
270                 if let EvalErrorKind::NoMirFor(ref path) = err.kind {
271                     return Err(
272                         ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
273                             .into(),
274                     );
275                 }
276                 return Err(err);
277             }
278         };
279         let (return_place, return_to_block) = match destination {
280             Some((place, block)) => (place, StackPopCleanup::Goto(block)),
281             None => (Place::undef(), StackPopCleanup::None),
282         };
283
284         ecx.push_stack_frame(
285             instance,
286             span,
287             mir,
288             return_place,
289             return_to_block,
290         )?;
291
292         Ok(false)
293     }
294
295
296     fn call_intrinsic<'a>(
297         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
298         instance: ty::Instance<'tcx>,
299         args: &[ValTy<'tcx>],
300         dest: Place,
301         dest_layout: layout::TyLayout<'tcx>,
302         target: mir::BasicBlock,
303     ) -> EvalResult<'tcx> {
304         let substs = instance.substs;
305
306         let intrinsic_name = &ecx.tcx.item_name(instance.def_id()).as_str()[..];
307         match intrinsic_name {
308             "min_align_of" => {
309                 let elem_ty = substs.type_at(0);
310                 let elem_align = ecx.layout_of(elem_ty)?.align.abi();
311                 let align_val = Scalar::Bits {
312                     bits: elem_align as u128,
313                     size: dest_layout.size.bytes() as u8,
314                 };
315                 ecx.write_scalar(dest, align_val, dest_layout.ty)?;
316             }
317
318             "size_of" => {
319                 let ty = substs.type_at(0);
320                 let size = ecx.layout_of(ty)?.size.bytes() as u128;
321                 let size_val = Scalar::Bits {
322                     bits: size,
323                     size: dest_layout.size.bytes() as u8,
324                 };
325                 ecx.write_scalar(dest, size_val, dest_layout.ty)?;
326             }
327
328             "type_id" => {
329                 let ty = substs.type_at(0);
330                 let type_id = ecx.tcx.type_id_hash(ty) as u128;
331                 let id_val = Scalar::Bits {
332                     bits: type_id,
333                     size: dest_layout.size.bytes() as u8,
334                 };
335                 ecx.write_scalar(dest, id_val, dest_layout.ty)?;
336             }
337             "ctpop" | "cttz" | "cttz_nonzero" | "ctlz" | "ctlz_nonzero" | "bswap" => {
338                 let ty = substs.type_at(0);
339                 let layout_of = ecx.layout_of(ty)?;
340                 let bits = ecx.value_to_scalar(args[0])?.to_bits(layout_of.size)?;
341                 let kind = match layout_of.abi {
342                     ty::layout::Abi::Scalar(ref scalar) => scalar.value,
343                     _ => Err(::rustc::mir::interpret::EvalErrorKind::TypeNotPrimitive(ty))?,
344                 };
345                 let out_val = if intrinsic_name.ends_with("_nonzero") {
346                     if bits == 0 {
347                         return err!(Intrinsic(format!("{} called on 0", intrinsic_name)));
348                     }
349                     numeric_intrinsic(intrinsic_name.trim_right_matches("_nonzero"), bits, kind)?
350                 } else {
351                     numeric_intrinsic(intrinsic_name, bits, kind)?
352                 };
353                 ecx.write_scalar(dest, out_val, ty)?;
354             }
355
356             name => return Err(
357                 ConstEvalError::NeedsRfc(format!("calling intrinsic `{}`", name)).into()
358             ),
359         }
360
361         ecx.goto_block(target);
362
363         // Since we pushed no stack frame, the main loop will act
364         // as if the call just completed and it's returning to the
365         // current frame.
366         Ok(())
367     }
368
369     fn try_ptr_op<'a>(
370         _ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
371         _bin_op: mir::BinOp,
372         left: Scalar,
373         _left_ty: Ty<'tcx>,
374         right: Scalar,
375         _right_ty: Ty<'tcx>,
376     ) -> EvalResult<'tcx, Option<(Scalar, bool)>> {
377         if left.is_bits() && right.is_bits() {
378             Ok(None)
379         } else {
380             Err(
381                 ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
382             )
383         }
384     }
385
386     fn mark_static_initialized<'a>(
387         _mem: &mut Memory<'a, 'mir, 'tcx, Self>,
388         _id: AllocId,
389         _mutability: Mutability,
390     ) -> EvalResult<'tcx, bool> {
391         Ok(false)
392     }
393
394     fn init_static<'a>(
395         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
396         cid: GlobalId<'tcx>,
397     ) -> EvalResult<'tcx, AllocId> {
398         Ok(ecx
399             .tcx
400             .alloc_map
401             .lock()
402             .intern_static(cid.instance.def_id()))
403     }
404
405     fn box_alloc<'a>(
406         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
407         _ty: Ty<'tcx>,
408         _dest: Place,
409     ) -> EvalResult<'tcx> {
410         Err(
411             ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
412         )
413     }
414
415     fn global_item_with_linkage<'a>(
416         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
417         _instance: ty::Instance<'tcx>,
418         _mutability: Mutability,
419     ) -> EvalResult<'tcx> {
420         Err(
421             ConstEvalError::NotConst("statics with `linkage` attribute".to_string()).into(),
422         )
423     }
424 }
425
426 pub fn const_val_field<'a, 'tcx>(
427     tcx: TyCtxt<'a, 'tcx, 'tcx>,
428     param_env: ty::ParamEnv<'tcx>,
429     instance: ty::Instance<'tcx>,
430     variant: Option<usize>,
431     field: mir::Field,
432     value: &'tcx ty::Const<'tcx>,
433 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
434     trace!("const_val_field: {:?}, {:?}, {:?}", instance, field, value);
435     let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
436     let result = (|| {
437         let ty = value.ty;
438         let value = ecx.const_to_value(value.val)?;
439         let layout = ecx.layout_of(ty)?;
440         let place = ecx.allocate_place_for_value(value, layout, variant)?;
441         let (place, layout) = ecx.place_field(place, field, layout)?;
442         let (ptr, align) = place.to_ptr_align();
443         let mut new_value = Value::ByRef(ptr.unwrap_or_err()?, align);
444         new_value = ecx.try_read_by_ref(new_value, layout.ty)?;
445         use rustc_data_structures::indexed_vec::Idx;
446         match (value, new_value) {
447             (Value::Scalar(_), Value::ByRef(..)) |
448             (Value::ScalarPair(..), Value::ByRef(..)) |
449             (Value::Scalar(_), Value::ScalarPair(..)) => bug!(
450                 "field {} of {:?} yielded {:?}",
451                 field.index(),
452                 value,
453                 new_value,
454             ),
455             _ => {},
456         }
457         Ok(value_to_const_value(&ecx, new_value, layout))
458     })();
459     result.map_err(|err| {
460         let (trace, span) = ecx.generate_stacktrace(None);
461         ConstEvalErr {
462             error: err,
463             stacktrace: trace,
464             span,
465         }.into()
466     })
467 }
468
469 pub fn const_variant_index<'a, 'tcx>(
470     tcx: TyCtxt<'a, 'tcx, 'tcx>,
471     param_env: ty::ParamEnv<'tcx>,
472     instance: ty::Instance<'tcx>,
473     val: &'tcx ty::Const<'tcx>,
474 ) -> EvalResult<'tcx, usize> {
475     trace!("const_variant_index: {:?}, {:?}", instance, val);
476     let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
477     let value = ecx.const_to_value(val.val)?;
478     let layout = ecx.layout_of(val.ty)?;
479     let (ptr, align) = match value {
480         Value::ScalarPair(..) | Value::Scalar(_) => {
481             let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?.into();
482             ecx.write_value_to_ptr(value, ptr, layout.align, val.ty)?;
483             (ptr, layout.align)
484         },
485         Value::ByRef(ptr, align) => (ptr, align),
486     };
487     let place = Place::from_scalar_ptr(ptr.into(), align);
488     ecx.read_discriminant_as_variant_index(place, layout)
489 }
490
491 pub fn const_value_to_allocation_provider<'a, 'tcx>(
492     tcx: TyCtxt<'a, 'tcx, 'tcx>,
493     val: &'tcx ty::Const<'tcx>,
494 ) -> &'tcx Allocation {
495     match val.val {
496         ConstValue::ByRef(alloc, offset) => {
497             assert_eq!(offset.bytes(), 0);
498             return alloc;
499         },
500         _ => ()
501     }
502     let result = || -> EvalResult<'tcx, &'tcx Allocation> {
503         let mut ecx = EvalContext::new(
504             tcx.at(DUMMY_SP),
505             ty::ParamEnv::reveal_all(),
506             CompileTimeEvaluator,
507             ());
508         let value = ecx.const_to_value(val.val)?;
509         let layout = ecx.layout_of(val.ty)?;
510         let ptr = ecx.memory.allocate(layout.size, layout.align, MemoryKind::Stack)?;
511         ecx.write_value_to_ptr(value, ptr.into(), layout.align, val.ty)?;
512         let alloc = ecx.memory.get(ptr.alloc_id)?;
513         Ok(tcx.intern_const_alloc(alloc.clone()))
514     };
515     result().expect("unable to convert ConstValue to Allocation")
516 }
517
518 pub fn const_eval_provider<'a, 'tcx>(
519     tcx: TyCtxt<'a, 'tcx, 'tcx>,
520     key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
521 ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> {
522     trace!("const eval: {:?}", key);
523     let cid = key.value;
524     let def_id = cid.instance.def.def_id();
525
526     if let Some(id) = tcx.hir.as_local_node_id(def_id) {
527         let tables = tcx.typeck_tables_of(def_id);
528         let span = tcx.def_span(def_id);
529
530         // Do match-check before building MIR
531         if tcx.check_match(def_id).is_err() {
532             return Err(ConstEvalErr {
533                 error: EvalErrorKind::CheckMatchError.into(),
534                 stacktrace: vec![],
535                 span,
536             }.into());
537         }
538
539         if let hir::BodyOwnerKind::Const = tcx.hir.body_owner_kind(id) {
540             tcx.mir_const_qualif(def_id);
541         }
542
543         // Do not continue into miri if typeck errors occurred; it will fail horribly
544         if tables.tainted_by_errors {
545             return Err(ConstEvalErr {
546                 error: EvalErrorKind::CheckMatchError.into(),
547                 stacktrace: vec![],
548                 span,
549             }.into());
550         }
551     };
552
553     let (res, ecx) = eval_body_and_ecx(tcx, cid, None, key.param_env);
554     res.and_then(|(mut val, _, layout)| {
555         if tcx.is_static(def_id).is_none() && cid.promoted.is_none() {
556             val = ecx.try_read_by_ref(val, layout.ty)?;
557         }
558         Ok(value_to_const_value(&ecx, val, layout))
559     }).map_err(|err| {
560         let (trace, span) = ecx.generate_stacktrace(None);
561         let err = ConstEvalErr {
562             error: err,
563             stacktrace: trace,
564             span,
565         };
566         if tcx.is_static(def_id).is_some() {
567             err.report_as_error(ecx.tcx, "could not evaluate static initializer");
568             if tcx.sess.err_count() == 0 {
569                 span_bug!(span, "static eval failure didn't emit an error: {:#?}", err);
570             }
571         }
572         err.into()
573     })
574 }
575
576 fn numeric_intrinsic<'tcx>(
577     name: &str,
578     bits: u128,
579     kind: Primitive,
580 ) -> EvalResult<'tcx, Scalar> {
581     let size = match kind {
582         Primitive::Int(integer, _) => integer.size(),
583         _ => bug!("invalid `{}` argument: {:?}", name, bits),
584     };
585     let extra = 128 - size.bits() as u128;
586     let bits_out = match name {
587         "ctpop" => bits.count_ones() as u128,
588         "ctlz" => bits.leading_zeros() as u128 - extra,
589         "cttz" => (bits << extra).trailing_zeros() as u128 - extra,
590         "bswap" => (bits << extra).swap_bytes(),
591         _ => bug!("not a numeric intrinsic: {}", name),
592     };
593     Ok(Scalar::Bits { bits: bits_out, size: size.bytes() as u8 })
594 }