From: Saleem Jaffer Date: Sat, 20 Jul 2019 13:27:46 +0000 (+0530) Subject: moving some variants from InterpError to EvalErrorPanic X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=90426ed6427f7a7a79eb47f60615d5a62ab1869f;hp=fd352b02e17d78f03345ebc3ffabe02b0cb04fd1;p=rust.git moving some variants from InterpError to EvalErrorPanic --- diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 9e216a14874..41ec2029c81 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -284,11 +284,6 @@ pub enum InterpError<'tcx, O> { Unimplemented(String), DerefFunctionPointer, ExecuteMemory, - BoundsCheck { len: O, index: O }, - Overflow(mir::BinOp), - OverflowNeg, - DivisionByZero, - RemainderByZero, Intrinsic(String), InvalidChar(u128), StackFrameLimitReached, @@ -332,7 +327,6 @@ pub enum InterpError<'tcx, O> { InfiniteLoop, } - pub type InterpResult<'tcx, T = ()> = Result>; impl<'tcx, O> InterpError<'tcx, O> { @@ -383,8 +377,6 @@ pub fn description(&self) -> &str { "tried to dereference a function pointer", ExecuteMemory => "tried to treat a memory pointer as a function pointer", - BoundsCheck{..} => - "array index out of bounds", Intrinsic(..) => "intrinsic failed", NoMirFor(..) => @@ -436,8 +428,32 @@ pub fn description(&self) -> &str { two", Unreachable => "entered unreachable code", - Panic { .. } => + Panic(EvalErrorPanic::Panic{..}) => "the evaluated program panicked", + Panic(EvalErrorPanic::BoundsCheck{..}) => + "array index out of bounds", + Panic(EvalErrorPanic::Overflow(mir::BinOp::Add)) => + "attempt to add with overflow", + Panic(EvalErrorPanic::Overflow(mir::BinOp::Sub)) => + "attempt to subtract with overflow", + Panic(EvalErrorPanic::Overflow(mir::BinOp::Mul)) => + "attempt to multiply with overflow", + Panic(EvalErrorPanic::Overflow(mir::BinOp::Div)) => + "attempt to divide with overflow", + Panic(EvalErrorPanic::Overflow(mir::BinOp::Rem)) => + "attempt to calculate the remainder with overflow", + Panic(EvalErrorPanic::OverflowNeg) => + "attempt to negate with overflow", + Panic(EvalErrorPanic::Overflow(mir::BinOp::Shr)) => + "attempt to shift right with overflow", + Panic(EvalErrorPanic::Overflow(mir::BinOp::Shl)) => + "attempt to shift left with overflow", + Panic(EvalErrorPanic::Overflow(op)) => + bug!("{:?} cannot overflow", op), + Panic(EvalErrorPanic::DivisionByZero) => + "attempt to divide by zero", + Panic(EvalErrorPanic::RemainderByZero) => + "attempt to calculate the remainder with a divisor of zero", ReadFromReturnPointer => "tried to read from the return pointer", PathNotFound(_) => @@ -450,17 +466,6 @@ pub fn description(&self) -> &str { "encountered overly generic constant", ReferencedConstant => "referenced constant has errors", - Overflow(mir::BinOp::Add) => "attempt to add with overflow", - Overflow(mir::BinOp::Sub) => "attempt to subtract with overflow", - Overflow(mir::BinOp::Mul) => "attempt to multiply with overflow", - Overflow(mir::BinOp::Div) => "attempt to divide with overflow", - Overflow(mir::BinOp::Rem) => "attempt to calculate the remainder with overflow", - OverflowNeg => "attempt to negate with overflow", - Overflow(mir::BinOp::Shr) => "attempt to shift right with overflow", - Overflow(mir::BinOp::Shl) => "attempt to shift left with overflow", - Overflow(op) => bug!("{:?} cannot overflow", op), - DivisionByZero => "attempt to divide by zero", - RemainderByZero => "attempt to calculate the remainder with a divisor of zero", GeneratorResumedAfterReturn => "generator resumed after completion", GeneratorResumedAfterPanic => "generator resumed after panicking", InfiniteLoop => @@ -507,8 +512,6 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { callee_ty, caller_ty), FunctionArgCountMismatch => write!(f, "tried to call a function with incorrect number of arguments"), - BoundsCheck { ref len, ref index } => - write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index), ReallocatedWrongMemoryKind(ref old, ref new) => write!(f, "tried to reallocate memory from {} to {}", old, new), DeallocatedWrongMemoryKind(ref old, ref new) => @@ -532,8 +535,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "incorrect alloc info: expected size {} and align {}, \ got size {} and align {}", size.bytes(), align.bytes(), size2.bytes(), align2.bytes()), - Panic { .. } => - write!(f, "the evaluated program panicked"), + Panic(EvalErrorPanic::Panic { ref msg, line, col, ref file }) => + write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col), + Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) => + write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index), InvalidDiscriminant(val) => write!(f, "encountered invalid enum discriminant {}", val), Exit(code) => diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs index a17bc1f6728..814cd90343c 100644 --- a/src/librustc/mir/interpret/pointer.rs +++ b/src/librustc/mir/interpret/pointer.rs @@ -5,7 +5,7 @@ use rustc_macros::HashStable; use super::{ - AllocId, InterpResult, + AllocId, InterpResult, EvalErrorPanic }; /// Used by `check_in_alloc` to indicate context of check @@ -76,13 +76,13 @@ fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) { #[inline] fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> { let (res, over) = self.overflowing_offset(val, i); - if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } + if over { err!(Panic(EvalErrorPanic::Overflow(mir::BinOp::Add))) } else { Ok(res) } } #[inline] fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> { let (res, over) = self.overflowing_signed_offset(val, i128::from(i)); - if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } + if over { err!(Panic(EvalErrorPanic::Overflow(mir::BinOp::Add))) } else { Ok(res) } } } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 783964c701a..ca1111d7fbf 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -7,7 +7,7 @@ use crate::hir::def::{CtorKind, Namespace}; use crate::hir::def_id::DefId; use crate::hir::{self, InlineAsm as HirInlineAsm}; -use crate::mir::interpret::{ConstValue, InterpError, Scalar}; +use crate::mir::interpret::{ConstValue, EvalErrorPanic, InterpError::Panic, Scalar}; use crate::mir::visit::MirVisitable; use crate::rustc_serialize as serialize; use crate::ty::adjustment::PointerCast; @@ -3087,11 +3087,11 @@ fn super_fold_with>(&self, folder: &mut F) -> Self { } } Assert { ref cond, expected, ref msg, target, cleanup } => { - let msg = if let InterpError::BoundsCheck { ref len, ref index } = *msg { - InterpError::BoundsCheck { + let msg = if let Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) = *msg { + Panic(EvalErrorPanic::BoundsCheck { len: len.fold_with(folder), index: index.fold_with(folder), - } + }) } else { msg.clone() }; @@ -3132,7 +3132,7 @@ fn super_visit_with>(&self, visitor: &mut V) -> bool { } Assert { ref cond, ref msg, .. } => { if cond.visit_with(visitor) { - if let InterpError::BoundsCheck { ref len, ref index } = *msg { + if let Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) = *msg { len.visit_with(visitor) || index.visit_with(visitor) } else { false diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index babce812d4a..54f987e2cf0 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -514,7 +514,8 @@ fn super_assert_message(&mut self, msg: & $($mutability)? AssertMessage<'tcx>, location: Location) { use crate::mir::interpret::InterpError::*; - if let BoundsCheck { len, index } = msg { + use crate::mir::interpret::EvalErrorPanic::BoundsCheck; + if let Panic(BoundsCheck { len, index }) = msg { self.visit_operand(len, location); self.visit_operand(index, location); } diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index 941166ccfab..beb6b9421ce 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -2,7 +2,7 @@ use rustc::ty::{self, Ty, TypeFoldable, Instance}; use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, FnTypeExt}; use rustc::mir::{self, Place, PlaceBase, Static, StaticKind}; -use rustc::mir::interpret::InterpError; +use rustc::mir::interpret::{InterpError, EvalErrorPanic}; use rustc_target::abi::call::{ArgType, FnType, PassMode, IgnoreMode}; use rustc_target::spec::abi::Abi; use crate::base; @@ -368,7 +368,7 @@ fn codegen_assert_terminator<'b>( // checked operation, just a comparison with the minimum // value, so we have to check for the assert message. if !bx.check_overflow() { - if let mir::interpret::InterpError::OverflowNeg = *msg { + if let InterpError::Panic(EvalErrorPanic::OverflowNeg) = *msg { const_cond = Some(expected); } } @@ -403,7 +403,7 @@ fn codegen_assert_terminator<'b>( // Put together the arguments to the panic entry point. let (lang_item, args) = match *msg { - InterpError::BoundsCheck { ref len, ref index } => { + InterpError::Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) => { let len = self.codegen_operand(&mut bx, len).immediate(); let index = self.codegen_operand(&mut bx, index).immediate(); diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 5851cd81788..86f2c07e6b9 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -734,8 +734,8 @@ fn visit_terminator_entry( cleanup: _, } => { self.consume_operand(loc, (cond, span), flow_state); - use rustc::mir::interpret::InterpError::BoundsCheck; - if let BoundsCheck { ref len, ref index } = *msg { + use rustc::mir::interpret::{InterpError::Panic, EvalErrorPanic}; + if let Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) = *msg { self.consume_operand(loc, (len, span), flow_state); self.consume_operand(loc, (index, span), flow_state); } diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs index c7b4a403052..51a00d2f0eb 100644 --- a/src/librustc_mir/borrow_check/nll/invalidation.rs +++ b/src/librustc_mir/borrow_check/nll/invalidation.rs @@ -207,8 +207,8 @@ fn visit_terminator_kind( cleanup: _, } => { self.consume_operand(location, cond); - use rustc::mir::interpret::InterpError::BoundsCheck; - if let BoundsCheck { ref len, ref index } = *msg { + use rustc::mir::interpret::{InterpError::Panic, EvalErrorPanic::BoundsCheck}; + if let Panic(BoundsCheck { ref len, ref index }) = *msg { self.consume_operand(location, len); self.consume_operand(location, index); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index cdbbe1d02bd..79020fdb4a5 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -28,7 +28,7 @@ use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin}; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc::mir::interpret::{InterpError::BoundsCheck, ConstValue}; +use rustc::mir::interpret::{InterpError::Panic, ConstValue, EvalErrorPanic}; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor, NonMutatingUseContext}; use rustc::mir::*; @@ -1589,7 +1589,7 @@ fn check_terminator( span_mirbug!(self, term, "bad Assert ({:?}, not bool", cond_ty); } - if let BoundsCheck { ref len, ref index } = *msg { + if let Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) = *msg { if len.ty(body, tcx) != tcx.types.usize { span_mirbug!(self, len, "bounds-check length non-usize {:?}", len) } diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs index 82accb47437..f342761e25d 100644 --- a/src/librustc_mir/build/expr/as_place.rs +++ b/src/librustc_mir/build/expr/as_place.rs @@ -4,7 +4,7 @@ use crate::build::ForGuard::{OutsideGuard, RefWithinGuard}; use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::hair::*; -use rustc::mir::interpret::InterpError::BoundsCheck; +use rustc::mir::interpret::{InterpError::Panic, EvalErrorPanic::BoundsCheck}; use rustc::mir::*; use rustc::ty::{CanonicalUserTypeAnnotation, Variance}; @@ -105,10 +105,10 @@ fn expr_as_place( ), ); - let msg = BoundsCheck { + let msg = Panic(BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(idx)), - }; + }); let success = this.assert(block, Operand::Move(lt), true, msg, expr_span); success.and(slice.index(idx)) } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 56c518a6d57..10bf48a85d0 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -7,7 +7,7 @@ use crate::build::{BlockAnd, BlockAndExtension, Builder}; use crate::hair::*; use rustc::middle::region; -use rustc::mir::interpret::InterpError; +use rustc::mir::interpret::{InterpError::Panic, EvalErrorPanic}; use rustc::mir::*; use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, UpvarSubsts}; use syntax_pos::Span; @@ -101,7 +101,7 @@ fn expr_as_rvalue( block, Operand::Move(is_min), false, - InterpError::OverflowNeg, + Panic(EvalErrorPanic::OverflowNeg), expr_span, ); } @@ -401,7 +401,7 @@ pub fn build_binary_op( let val = result_value.clone().field(val_fld, ty); let of = result_value.field(of_fld, bool_ty); - let err = InterpError::Overflow(op); + let err = Panic(EvalErrorPanic::Overflow(op)); block = self.assert(block, Operand::Move(of), false, err, span); @@ -412,9 +412,9 @@ pub fn build_binary_op( // and 2. there are two possible failure cases, divide-by-zero and overflow. let (zero_err, overflow_err) = if op == BinOp::Div { - (InterpError::DivisionByZero, InterpError::Overflow(op)) + (Panic(EvalErrorPanic::DivisionByZero), Panic(EvalErrorPanic::Overflow(op))) } else { - (InterpError::RemainderByZero, InterpError::Overflow(op)) + (Panic(EvalErrorPanic::RemainderByZero), Panic(EvalErrorPanic::Overflow(op))) }; // Check for / 0 diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 20180c9cba5..0932b468a50 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -2,7 +2,7 @@ use rustc::ty::{self, layout::TyLayout}; use syntax::ast::FloatTy; use rustc_apfloat::Float; -use rustc::mir::interpret::{InterpResult, Scalar}; +use rustc::mir::interpret::{InterpResult, EvalErrorPanic, Scalar}; use super::{InterpCx, PlaceTy, Immediate, Machine, ImmTy}; @@ -173,8 +173,8 @@ fn binary_int_op( return Ok((Scalar::from_bool(op(&l, &r)), false)); } let op: Option (i128, bool)> = match bin_op { - Div if r == 0 => return err!(DivisionByZero), - Rem if r == 0 => return err!(RemainderByZero), + Div if r == 0 => return err!(Panic(EvalErrorPanic::DivisionByZero)), + Rem if r == 0 => return err!(Panic(EvalErrorPanic::RemainderByZero)), Div => Some(i128::overflowing_div), Rem => Some(i128::overflowing_rem), Add => Some(i128::overflowing_add), @@ -231,8 +231,8 @@ fn binary_int_op( Add => u128::overflowing_add, Sub => u128::overflowing_sub, Mul => u128::overflowing_mul, - Div if r == 0 => return err!(DivisionByZero), - Rem if r == 0 => return err!(RemainderByZero), + Div if r == 0 => return err!(Panic(EvalErrorPanic::DivisionByZero)), + Rem if r == 0 => return err!(Panic(EvalErrorPanic::RemainderByZero)), Div => u128::overflowing_div, Rem => u128::overflowing_rem, _ => bug!(), diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 68382071b4a..9887095882f 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -13,7 +13,7 @@ use super::{ GlobalId, AllocId, Allocation, Scalar, InterpResult, Pointer, PointerArithmetic, - InterpCx, Machine, AllocMap, AllocationExtra, + InterpCx, Machine, AllocMap, AllocationExtra, EvalErrorPanic, RawConst, Immediate, ImmTy, ScalarMaybeUndef, Operand, OpTy, MemoryKind, LocalValue }; @@ -356,7 +356,7 @@ pub fn mplace_field( // This can be violated because this runs during promotion on code where the // type system has not yet ensured that such things don't happen. debug!("tried to access element {} of array/slice with length {}", field, len); - return err!(BoundsCheck { len, index: field }); + return err!(Panic(EvalErrorPanic::BoundsCheck { len, index: field })); } stride * field } diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 75690b4d361..ae1d8efa753 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -7,7 +7,7 @@ use rustc_target::spec::abi::Abi; use super::{ - InterpResult, PointerArithmetic, InterpError, Scalar, + InterpResult, PointerArithmetic, InterpError, Scalar, EvalErrorPanic, InterpCx, Machine, Immediate, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal, }; @@ -137,19 +137,23 @@ pub(super) fn eval_terminator( // Compute error message use rustc::mir::interpret::InterpError::*; return match *msg { - BoundsCheck { ref len, ref index } => { + Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) => { let len = self.read_immediate(self.eval_operand(len, None)?) .expect("can't eval len").to_scalar()? .to_bits(self.memory().pointer_size())? as u64; let index = self.read_immediate(self.eval_operand(index, None)?) .expect("can't eval index").to_scalar()? .to_bits(self.memory().pointer_size())? as u64; - err!(BoundsCheck { len, index }) + err!(Panic(EvalErrorPanic::BoundsCheck { len, index })) } - Overflow(op) => Err(Overflow(op).into()), - OverflowNeg => Err(OverflowNeg.into()), - DivisionByZero => Err(DivisionByZero.into()), - RemainderByZero => Err(RemainderByZero.into()), + Panic(EvalErrorPanic::Overflow(op)) => + Err(Panic(EvalErrorPanic::Overflow(op)).into()), + Panic(EvalErrorPanic::OverflowNeg) => + Err(Panic(EvalErrorPanic::OverflowNeg).into()), + Panic(EvalErrorPanic::DivisionByZero) => + Err(Panic(EvalErrorPanic::DivisionByZero).into()), + Panic(EvalErrorPanic::RemainderByZero) => + Err(Panic(EvalErrorPanic::RemainderByZero).into()), GeneratorResumedAfterReturn | GeneratorResumedAfterPanic => unimplemented!(), _ => bug!(), diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 29480f88fce..9d9e47ecaf4 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -13,7 +13,7 @@ use rustc::mir::visit::{ Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext, }; -use rustc::mir::interpret::{InterpError, Scalar, GlobalId, InterpResult}; +use rustc::mir::interpret::{InterpError::Panic, Scalar, GlobalId, InterpResult, EvalErrorPanic}; use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt}; use syntax_pos::{Span, DUMMY_SP}; use rustc::ty::subst::InternalSubsts; @@ -339,12 +339,12 @@ fn use_ecx( // FIXME: implement => {}, - | Panic { .. } - | BoundsCheck{..} - | Overflow(_) - | OverflowNeg - | DivisionByZero - | RemainderByZero + | Panic(EvalErrorPanic::Panic { .. }) + | Panic(EvalErrorPanic::BoundsCheck{..}) + | Panic(EvalErrorPanic::Overflow(_)) + | Panic(EvalErrorPanic::OverflowNeg) + | Panic(EvalErrorPanic::DivisionByZero) + | Panic(EvalErrorPanic::RemainderByZero) => { diagnostic.report_as_lint( self.ecx.tcx, @@ -522,7 +522,7 @@ fn const_prop( // Need to do overflow check here: For actual CTFE, MIR // generation emits code that does this before calling the op. if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { - return err!(OverflowNeg); + return err!(Panic(EvalErrorPanic::OverflowNeg)); } } UnOp::Not => { @@ -600,7 +600,7 @@ fn const_prop( ) } else { if overflow { - let err = InterpError::Overflow(op).into(); + let err = Panic(EvalErrorPanic::Overflow(op)).into(); let _: Option<()> = self.use_ecx(source_info, |_| Err(err)); return None; } @@ -839,11 +839,11 @@ fn visit_terminator( .expect("some part of a failing const eval must be local"); use rustc::mir::interpret::InterpError::*; let msg = match msg { - Overflow(_) | - OverflowNeg | - DivisionByZero | - RemainderByZero => msg.description().to_owned(), - BoundsCheck { ref len, ref index } => { + Panic(EvalErrorPanic::Overflow(_)) | + Panic(EvalErrorPanic::OverflowNeg) | + Panic(EvalErrorPanic::DivisionByZero) | + Panic(EvalErrorPanic::RemainderByZero) => msg.description().to_owned(), + Panic(EvalErrorPanic::BoundsCheck { ref len, ref index }) => { let len = self .eval_operand(len, source_info) .expect("len must be const");