From 2e39b9c51f8a94662bf0c7d1f3411ebade7bfeb6 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 23 May 2019 22:17:45 +0200 Subject: [PATCH 1/8] Make find_local iterate instead of recurse --- .../dataflow/impls/borrowed_locals.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/dataflow/impls/borrowed_locals.rs b/src/librustc_mir/dataflow/impls/borrowed_locals.rs index 42c2387b7052d..65cbc83fbcb84 100644 --- a/src/librustc_mir/dataflow/impls/borrowed_locals.rs +++ b/src/librustc_mir/dataflow/impls/borrowed_locals.rs @@ -91,16 +91,19 @@ struct BorrowedLocalsVisitor<'b, 'c: 'b> { } fn find_local<'tcx>(place: &Place<'tcx>) -> Option { - match *place { - Place::Base(PlaceBase::Local(l)) => Some(l), - Place::Base(PlaceBase::Static(..)) => None, - Place::Projection(ref proj) => { - match proj.elem { - ProjectionElem::Deref => None, - _ => find_local(&proj.base) + place.iterate(|place_base, place_projection| { + for proj in place_projection { + if proj.elem == ProjectionElem::Deref { + return None; } } - } + + if let PlaceBase::Local(local) = place_base { + Some(*local) + } else { + None + } + }) } impl<'tcx, 'b, 'c> Visitor<'tcx> for BorrowedLocalsVisitor<'b, 'c> { From 34314caea39300276375f3cd948e09a37bec6873 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 24 May 2019 01:15:08 +0200 Subject: [PATCH 2/8] Make find iterate instead of recurse --- src/librustc_mir/dataflow/move_paths/mod.rs | 27 +++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/dataflow/move_paths/mod.rs b/src/librustc_mir/dataflow/move_paths/mod.rs index 6d619793160fb..7aaf44a8a890e 100644 --- a/src/librustc_mir/dataflow/move_paths/mod.rs +++ b/src/librustc_mir/dataflow/move_paths/mod.rs @@ -241,21 +241,22 @@ impl MovePathLookup { // unknown place, but will rather return the nearest available // parent. pub fn find(&self, place: &Place<'tcx>) -> LookupResult { - match *place { - Place::Base(PlaceBase::Local(local)) => LookupResult::Exact(self.locals[local]), - Place::Base(PlaceBase::Static(..)) => LookupResult::Parent(None), - Place::Projection(ref proj) => { - match self.find(&proj.base) { - LookupResult::Exact(base_path) => { - match self.projections.get(&(base_path, proj.elem.lift())) { - Some(&subpath) => LookupResult::Exact(subpath), - None => LookupResult::Parent(Some(base_path)) - } - } - inexact => inexact + place.iterate(|place_base, place_projection| { + let mut result = match place_base { + PlaceBase::Local(local) => self.locals[*local], + PlaceBase::Static(..) => return LookupResult::Parent(None), + }; + + for proj in place_projection { + if let Some(&subpath) = self.projections.get(&(result, proj.elem.lift())) { + result = subpath; + } else { + return LookupResult::Parent(Some(result)); } } - } + + LookupResult::Exact(result) + }) } pub fn find_local(&self, local: Local) -> MovePathIndex { From 03dc30d3d523753b44daab007d67e160a9a9d794 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 23 May 2019 21:41:10 +0200 Subject: [PATCH 3/8] Make sanitize_place iterate instead of recurse --- .../borrow_check/nll/type_check/mod.rs | 153 +++++++++--------- 1 file changed, 78 insertions(+), 75 deletions(-) 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 4589e4ef036a4..52c8f15c44bb9 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -29,7 +29,7 @@ use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionV use rustc::infer::type_variable::TypeVariableOrigin; use rustc::mir::interpret::{InterpError::BoundsCheck, ConstValue}; use rustc::mir::tcx::PlaceTy; -use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext}; +use rustc::mir::visit::{PlaceContext, Visitor, NonMutatingUseContext}; use rustc::mir::*; use rustc::traits::query::type_op; use rustc::traits::query::type_op::custom::CustomTypeOp; @@ -447,92 +447,95 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { context: PlaceContext, ) -> PlaceTy<'tcx> { debug!("sanitize_place: {:?}", place); - let place_ty = match place { - Place::Base(PlaceBase::Local(index)) => - PlaceTy::from_ty(self.mir.local_decls[*index].ty), - Place::Base(PlaceBase::Static(box Static { kind, ty: sty })) => { - let sty = self.sanitize_type(place, sty); - let check_err = - |verifier: &mut TypeVerifier<'a, 'b, 'gcx, 'tcx>, - place: &Place<'tcx>, - ty, - sty| { - if let Err(terr) = verifier.cx.eq_types( - sty, - ty, - location.to_locations(), - ConstraintCategory::Boring, - ) { - span_mirbug!( - verifier, - place, - "bad promoted type ({:?}: {:?}): {:?}", - ty, - sty, - terr - ); + + place.iterate(|place_base, place_projection| { + let mut place_ty = match place_base { + PlaceBase::Local(index) => + PlaceTy::from_ty(self.mir.local_decls[*index].ty), + PlaceBase::Static(box Static { kind, ty: sty }) => { + let sty = self.sanitize_type(place, sty); + let check_err = + |verifier: &mut TypeVerifier<'a, 'b, 'gcx, 'tcx>, + place: &Place<'tcx>, + ty, + sty| { + if let Err(terr) = verifier.cx.eq_types( + sty, + ty, + location.to_locations(), + ConstraintCategory::Boring, + ) { + span_mirbug!( + verifier, + place, + "bad promoted type ({:?}: {:?}): {:?}", + ty, + sty, + terr + ); + }; }; - }; - match kind { - StaticKind::Promoted(promoted) => { - if !self.errors_reported { - let promoted_mir = &self.mir.promoted[*promoted]; - self.sanitize_promoted(promoted_mir, location); - - let promoted_ty = promoted_mir.return_ty(); - check_err(self, place, promoted_ty, sty); + match kind { + StaticKind::Promoted(promoted) => { + if !self.errors_reported { + let promoted_mir = &self.mir.promoted[*promoted]; + self.sanitize_promoted(promoted_mir, location); + + let promoted_ty = promoted_mir.return_ty(); + check_err(self, place, promoted_ty, sty); + } } - } - StaticKind::Static(def_id) => { - let ty = self.tcx().type_of(*def_id); - let ty = self.cx.normalize(ty, location); + StaticKind::Static(def_id) => { + let ty = self.tcx().type_of(*def_id); + let ty = self.cx.normalize(ty, location); - check_err(self, place, ty, sty); + check_err(self, place, ty, sty); + } } + PlaceTy::from_ty(sty) + } + }; + + // FIXME use place_projection.is_empty() when is available + if let Place::Base(_) = place { + if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { + let tcx = self.tcx(); + let trait_ref = ty::TraitRef { + def_id: tcx.lang_items().copy_trait().unwrap(), + substs: tcx.mk_substs_trait(place_ty.ty, &[]), + }; + + // In order to have a Copy operand, the type T of the + // value must be Copy. Note that we prove that T: Copy, + // rather than using the `is_copy_modulo_regions` + // test. This is important because + // `is_copy_modulo_regions` ignores the resulting region + // obligations and assumes they pass. This can result in + // bounds from Copy impls being unsoundly ignored (e.g., + // #29149). Note that we decide to use Copy before knowing + // whether the bounds fully apply: in effect, the rule is + // that if a value of some type could implement Copy, then + // it must. + self.cx.prove_trait_ref( + trait_ref, + location.to_locations(), + ConstraintCategory::CopyBound, + ); } - PlaceTy::from_ty(sty) } - Place::Projection(ref proj) => { - let base_context = if context.is_mutating_use() { - PlaceContext::MutatingUse(MutatingUseContext::Projection) - } else { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) - }; - let base_ty = self.sanitize_place(&proj.base, location, base_context); - if base_ty.variant_index.is_none() { - if base_ty.ty.references_error() { + + for proj in place_projection { + if place_ty.variant_index.is_none() { + if place_ty.ty.references_error() { assert!(self.errors_reported); return PlaceTy::from_ty(self.tcx().types.err); } } - self.sanitize_projection(base_ty, &proj.elem, place, location) + place_ty = self.sanitize_projection(place_ty, &proj.elem, place, location) } - }; - if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy) = context { - let tcx = self.tcx(); - let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().copy_trait().unwrap(), - substs: tcx.mk_substs_trait(place_ty.ty, &[]), - }; - // In order to have a Copy operand, the type T of the - // value must be Copy. Note that we prove that T: Copy, - // rather than using the `is_copy_modulo_regions` - // test. This is important because - // `is_copy_modulo_regions` ignores the resulting region - // obligations and assumes they pass. This can result in - // bounds from Copy impls being unsoundly ignored (e.g., - // #29149). Note that we decide to use Copy before knowing - // whether the bounds fully apply: in effect, the rule is - // that if a value of some type could implement Copy, then - // it must. - self.cx.prove_trait_ref( - trait_ref, - location.to_locations(), - ConstraintCategory::CopyBound, - ); - } - place_ty + place_ty + }) } fn sanitize_promoted(&mut self, promoted_mir: &'b Mir<'tcx>, location: Location) { From 96137798556e662e9bb744fb58055e06147b9330 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 23 May 2019 21:43:21 +0200 Subject: [PATCH 4/8] Make borrow_of_local_data iterate instead of recurse --- src/librustc_mir/borrow_check/path_utils.rs | 28 ++++++++++----------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs index 0c2a4ef45f104..caef8d8bc5a92 100644 --- a/src/librustc_mir/borrow_check/path_utils.rs +++ b/src/librustc_mir/borrow_check/path_utils.rs @@ -131,22 +131,20 @@ pub(super) fn is_active<'tcx>( /// Determines if a given borrow is borrowing local data /// This is called for all Yield statements on movable generators pub(super) fn borrow_of_local_data<'tcx>(place: &Place<'tcx>) -> bool { - match place { - Place::Base(PlaceBase::Static(..)) => false, - Place::Base(PlaceBase::Local(..)) => true, - Place::Projection(box proj) => { - match proj.elem { - // Reborrow of already borrowed data is ignored - // Any errors will be caught on the initial borrow - ProjectionElem::Deref => false, + place.iterate(|place_base, place_projection| { + match place_base { + PlaceBase::Static(..) => return false, + PlaceBase::Local(..) => {}, + } - // For interior references and downcasts, find out if the base is local - ProjectionElem::Field(..) - | ProjectionElem::Index(..) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Downcast(..) => borrow_of_local_data(&proj.base), + for proj in place_projection { + // Reborrow of already borrowed data is ignored + // Any errors will be caught on the initial borrow + if proj.elem == ProjectionElem::Deref { + return false; } } - } + + true + }) } From 10fe26962c644d6b1a381ecea93d6fe516bd861f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 23 May 2019 21:53:59 +0200 Subject: [PATCH 5/8] Make ignore_borrow iterate instead of recurse --- src/librustc_mir/borrow_check/place_ext.rs | 68 +++++++++++----------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/librustc_mir/borrow_check/place_ext.rs b/src/librustc_mir/borrow_check/place_ext.rs index cf9a6165d71a2..9ad0e936e1b2d 100644 --- a/src/librustc_mir/borrow_check/place_ext.rs +++ b/src/librustc_mir/borrow_check/place_ext.rs @@ -25,40 +25,36 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { mir: &Mir<'tcx>, locals_state_at_exit: &LocalsStateAtExit, ) -> bool { - match self { - // If a local variable is immutable, then we only need to track borrows to guard - // against two kinds of errors: - // * The variable being dropped while still borrowed (e.g., because the fn returns - // a reference to a local variable) - // * The variable being moved while still borrowed - // - // In particular, the variable cannot be mutated -- the "access checks" will fail -- - // so we don't have to worry about mutation while borrowed. - Place::Base(PlaceBase::Local(index)) => { - match locals_state_at_exit { - LocalsStateAtExit::AllAreInvalidated => false, - LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => { - let ignore = !has_storage_dead_or_moved.contains(*index) && - mir.local_decls[*index].mutability == Mutability::Not; - debug!("ignore_borrow: local {:?} => {:?}", index, ignore); - ignore + self.iterate(|place_base, place_projection| { + let ignore = match place_base { + // If a local variable is immutable, then we only need to track borrows to guard + // against two kinds of errors: + // * The variable being dropped while still borrowed (e.g., because the fn returns + // a reference to a local variable) + // * The variable being moved while still borrowed + // + // In particular, the variable cannot be mutated -- the "access checks" will fail -- + // so we don't have to worry about mutation while borrowed. + PlaceBase::Local(index) => { + match locals_state_at_exit { + LocalsStateAtExit::AllAreInvalidated => false, + LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved } => { + let ignore = !has_storage_dead_or_moved.contains(*index) && + mir.local_decls[*index].mutability == Mutability::Not; + debug!("ignore_borrow: local {:?} => {:?}", index, ignore); + ignore + } } } - } - Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. })) => - false, - Place::Base(PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. })) => { - tcx.is_mutable_static(*def_id) - } - Place::Projection(proj) => match proj.elem { - ProjectionElem::Field(..) - | ProjectionElem::Downcast(..) - | ProjectionElem::Subslice { .. } - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Index(_) => proj.base.ignore_borrow( - tcx, mir, locals_state_at_exit), + PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) => + false, + PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => { + tcx.is_mutable_static(*def_id) + } + }; - ProjectionElem::Deref => { + for proj in place_projection { + if proj.elem == ProjectionElem::Deref { let ty = proj.base.ty(mir, tcx).ty; match ty.sty { // For both derefs of raw pointers and `&T` @@ -71,11 +67,13 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> { // original path into a new variable and // borrowed *that* one, leaving the original // path unborrowed. - ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => true, - _ => proj.base.ignore_borrow(tcx, mir, locals_state_at_exit), + ty::RawPtr(..) | ty::Ref(_, _, hir::MutImmutable) => return true, + _ => {} } } - }, - } + } + + ignore + }) } } From 8173febf930121e954a4be09043482ee771c0794 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 24 May 2019 20:37:57 +0200 Subject: [PATCH 6/8] Avoid catch-all arm, use Base(PlaceBase::Static(_)) --- src/librustc_mir/interpret/operand.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 9c2b491925f47..11bf750e8c7c1 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -472,13 +472,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> let op = match *mir_place { Base(PlaceBase::Local(mir::RETURN_PLACE)) => return err!(ReadFromReturnPointer), Base(PlaceBase::Local(local)) => self.access_local(self.frame(), local, layout)?, + Base(PlaceBase::Static(_)) => self.eval_place_to_mplace(mir_place)?.into(), Projection(ref proj) => { let op = self.eval_place_to_op(&proj.base, None)?; self.operand_projection(op, &proj.elem)? } - - _ => self.eval_place_to_mplace(mir_place)?.into(), }; trace!("eval_place_to_op: got {:?}", *op); From 6d7a36231a902a57bd627c1f1f6cd658ea9ab717 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 24 May 2019 22:35:32 +0200 Subject: [PATCH 7/8] Make eval_place_to_mplace take PlaceBase::Static --- src/librustc_mir/interpret/operand.rs | 10 +++++---- src/librustc_mir/interpret/place.rs | 30 +++++++++++++-------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 11bf750e8c7c1..669e00c1f01ed 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -469,12 +469,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { use rustc::mir::Place::*; use rustc::mir::PlaceBase; - let op = match *mir_place { + let op = match mir_place { Base(PlaceBase::Local(mir::RETURN_PLACE)) => return err!(ReadFromReturnPointer), - Base(PlaceBase::Local(local)) => self.access_local(self.frame(), local, layout)?, - Base(PlaceBase::Static(_)) => self.eval_place_to_mplace(mir_place)?.into(), + Base(PlaceBase::Local(local)) => self.access_local(self.frame(), *local, layout)?, + Base(PlaceBase::Static(place_static)) => { + self.eval_static_to_mplace(place_static)?.into() + } - Projection(ref proj) => { + Projection(proj) => { let op = self.eval_place_to_op(&proj.base, None)?; self.operand_projection(op, &proj.elem)? } diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 0cc480028161f..65e5e23e38424 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -562,15 +562,14 @@ where /// Evaluate statics and promoteds to an `MPlace`. Used to share some code between /// `eval_place` and `eval_place_to_op`. - pub(super) fn eval_place_to_mplace( + pub(super) fn eval_static_to_mplace( &self, - mir_place: &mir::Place<'tcx> + place_static: &mir::Static<'tcx> ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { - use rustc::mir::Place::*; - use rustc::mir::PlaceBase; - use rustc::mir::{Static, StaticKind}; - Ok(match *mir_place { - Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(promoted), .. })) => { + use rustc::mir::StaticKind; + + Ok(match place_static.kind { + StaticKind::Promoted(promoted) => { let instance = self.frame().instance; self.const_eval_raw(GlobalId { instance, @@ -578,7 +577,8 @@ where })? } - Base(PlaceBase::Static(box Static { kind: StaticKind::Static(def_id), ty })) => { + StaticKind::Static(def_id) => { + let ty = place_static.ty; assert!(!ty.needs_subst()); let layout = self.layout_of(ty)?; let instance = ty::Instance::mono(*self.tcx, def_id); @@ -600,8 +600,6 @@ where let alloc = self.tcx.alloc_map.lock().intern_static(cid.instance.def_id()); MPlaceTy::from_aligned_ptr(Pointer::from(alloc).with_default_tag(), layout) } - - _ => bug!("eval_place_to_mplace called on {:?}", mir_place), }) } @@ -613,7 +611,7 @@ where ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { use rustc::mir::Place::*; use rustc::mir::PlaceBase; - let place = match *mir_place { + let place = match mir_place { Base(PlaceBase::Local(mir::RETURN_PLACE)) => match self.frame().return_place { Some(return_place) => // We use our layout to verify our assumption; caller will validate @@ -628,17 +626,19 @@ where // This works even for dead/uninitialized locals; we check further when writing place: Place::Local { frame: self.cur_frame(), - local, + local: *local, }, - layout: self.layout_of_local(self.frame(), local, None)?, + layout: self.layout_of_local(self.frame(), *local, None)?, }, - Projection(ref proj) => { + Projection(proj) => { let place = self.eval_place(&proj.base)?; self.place_projection(place, &proj.elem)? } - _ => self.eval_place_to_mplace(mir_place)?.into(), + Base(PlaceBase::Static(place_static)) => { + self.eval_static_to_mplace(place_static)?.into() + } }; self.dump_place(place.place); From e38b399d03605f68ddbec50bf31e2f6fb12d278d Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 24 May 2019 01:28:41 +0200 Subject: [PATCH 8/8] Make eval_place_to_op iterate instead of recurse --- src/librustc_mir/interpret/operand.rs | 37 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 669e00c1f01ed..a674389a93695 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -467,23 +467,34 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> mir_place: &mir::Place<'tcx>, layout: Option>, ) -> EvalResult<'tcx, OpTy<'tcx, M::PointerTag>> { - use rustc::mir::Place::*; + use rustc::mir::Place; use rustc::mir::PlaceBase; - let op = match mir_place { - Base(PlaceBase::Local(mir::RETURN_PLACE)) => return err!(ReadFromReturnPointer), - Base(PlaceBase::Local(local)) => self.access_local(self.frame(), *local, layout)?, - Base(PlaceBase::Static(place_static)) => { - self.eval_static_to_mplace(place_static)?.into() - } - Projection(proj) => { - let op = self.eval_place_to_op(&proj.base, None)?; - self.operand_projection(op, &proj.elem)? + mir_place.iterate(|place_base, place_projection| { + let mut op = match place_base { + PlaceBase::Local(mir::RETURN_PLACE) => return err!(ReadFromReturnPointer), + PlaceBase::Local(local) => { + // FIXME use place_projection.is_empty() when is available + let layout = if let Place::Base(_) = mir_place { + layout + } else { + None + }; + + self.access_local(self.frame(), *local, layout)? + } + PlaceBase::Static(place_static) => { + self.eval_static_to_mplace(place_static)?.into() + } + }; + + for proj in place_projection { + op = self.operand_projection(op, &proj.elem)? } - }; - trace!("eval_place_to_op: got {:?}", *op); - Ok(op) + trace!("eval_place_to_op: got {:?}", *op); + Ok(op) + }) } /// Evaluate the operand, returning a place where you can then find the data.