diff --git a/rr_frontend/translation/src/body/mod.rs b/rr_frontend/translation/src/body/mod.rs
index 91e53cf310e6a02851bcf6507dcbc94a5f9813e3..52c25f7db2d73b54f9593c7bc2ab4226044fc7e1 100644
--- a/rr_frontend/translation/src/body/mod.rs
+++ b/rr_frontend/translation/src/body/mod.rs
@@ -37,7 +37,7 @@ use crate::traits::{registry, resolution};
 use crate::{base, consts, procedures, regions, search, traits, types};
 
 pub mod signature;
-pub mod translator;
+mod translation;
 
 /// Get the syntypes of function arguments for a procedure call.
 pub fn get_arg_syntypes_for_procedure_call<'tcx, 'def>(
@@ -98,22 +98,6 @@ pub fn get_arg_syntypes_for_procedure_call<'tcx, 'def>(
     Ok(syntypes)
 }
 
-// solve the constraints for the new_regions
-// we first identify what kinds of constraints these new regions are subject to
-#[derive(Debug)]
-enum CallRegionKind {
-    // this is just an intersection of local regions.
-    Intersection(HashSet<Region>),
-    // this is equal to a specific region
-    EqR(Region),
-}
-
-struct CallRegions {
-    pub early_regions: Vec<Region>,
-    pub late_regions: Vec<Region>,
-    pub classification: HashMap<Region, CallRegionKind>,
-}
-
 /// A scope of trait attributes mapping to Coq names to be used in a function's spec
 struct TraitSpecNameScope {
     attrs: HashMap<String, String>,
diff --git a/rr_frontend/translation/src/body/signature.rs b/rr_frontend/translation/src/body/signature.rs
index ea7e044fb6a2e97d61486c54a664c38caf6640de..a00d22a8b60416c38cb61d21b7bb2ee1770d62cc 100644
--- a/rr_frontend/translation/src/body/signature.rs
+++ b/rr_frontend/translation/src/body/signature.rs
@@ -26,7 +26,7 @@ use typed_arena::Arena;
 
 use crate::base::*;
 use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
-use crate::body::translator;
+use crate::body::translation;
 use crate::environment::borrowck::facts;
 use crate::environment::polonius_info::PoloniusInfo;
 use crate::environment::procedure::Procedure;
@@ -532,7 +532,7 @@ impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
         mut self,
         spec_arena: &'def Arena<radium::FunctionSpec<'def, radium::InnerFunctionSpec<'def>>>,
     ) -> Result<radium::Function<'def>, TranslationError<'tcx>> {
-        let translator = translator::TX::new(
+        let translator = translation::TX::new(
             self.env,
             self.procedure_registry,
             self.const_registry,
diff --git a/rr_frontend/translation/src/body/translation/basicblock.rs b/rr_frontend/translation/src/body/translation/basicblock.rs
new file mode 100644
index 0000000000000000000000000000000000000000..927fcf2d08a5abe828ba0b4135e8a9bfccca16c1
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/basicblock.rs
@@ -0,0 +1,257 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use super::TX;
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Translate a single basic block.
+    pub(super) fn translate_basic_block(
+        &mut self,
+        bb_idx: BasicBlock,
+        bb: &BasicBlockData<'tcx>,
+    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
+        // we translate from back to front, starting with the terminator, since Caesium statements
+        // have a continuation (the next statement to execute)
+
+        // first do the endlfts for the things right before the terminator
+        let mut idx = bb.statements.len();
+        let loc = Location {
+            block: bb_idx,
+            statement_index: idx,
+        };
+        let dying = self.info.get_dying_loans(loc);
+        // TODO zombie?
+        let _dying_zombie = self.info.get_dying_zombie_loans(loc);
+        let mut cont_stmt: radium::Stmt = self.translate_terminator(bb.terminator(), loc, dying)?;
+
+        //cont_stmt = self.prepend_endlfts(cont_stmt, loc, dying);
+        //cont_stmt = self.prepend_endlfts(cont_stmt, loc, dying_zombie);
+
+        for stmt in bb.statements.iter().rev() {
+            idx -= 1;
+            let loc = Location {
+                block: bb_idx,
+                statement_index: idx,
+            };
+
+            // get all dying loans, and emit endlfts for these.
+            // We loop over all predecessor locations, since some loans may end at the start of a
+            // basic block (in particular related to NLL stuff)
+            let pred = self.get_loc_predecessors(loc);
+            let mut dying_loans = HashSet::new();
+            for p in pred {
+                let dying_between = self.info.get_loans_dying_between(p, loc, false);
+                for l in &dying_between {
+                    dying_loans.insert(*l);
+                }
+                // also include zombies
+                let dying_between = self.info.get_loans_dying_between(p, loc, true);
+                for l in &dying_between {
+                    dying_loans.insert(*l);
+                }
+            }
+            // we prepend them before the current statement
+
+            match &stmt.kind {
+                StatementKind::Assign(b) => {
+                    let (plc, val) = b.as_ref();
+
+                    if (self.is_spec_closure_local(plc.local)?).is_some() {
+                        info!("skipping assignment to spec closure local: {:?}", plc);
+                    } else if let Some(rewritten_ty) = self.checked_op_temporaries.get(&plc.local) {
+                        // if this is a checked op, be sure to remember it
+                        info!("rewriting assignment to checked op: {:?}", plc);
+
+                        let synty = self.ty_translator.translate_type_to_syn_type(*rewritten_ty)?;
+
+                        let translated_val = self.translate_rvalue(loc, val)?;
+                        let translated_place = self.translate_place(plc)?;
+
+                        // this should be a temporary
+                        assert!(plc.projection.is_empty());
+
+                        let ot = synty.into();
+                        cont_stmt = radium::Stmt::Assign {
+                            ot,
+                            e1: translated_place,
+                            e2: translated_val,
+                            s: Box::new(cont_stmt),
+                        };
+                    } else {
+                        let plc_ty = self.get_type_of_place(plc);
+                        let rhs_ty = val.ty(&self.proc.get_mir().local_decls, self.env.tcx());
+
+                        let borrow_annots = regions::assignment::get_assignment_loan_annots(
+                            &mut self.inclusion_tracker, &self.ty_translator,
+                            loc, val);
+
+                        let plc_ty = self.get_type_of_place(plc);
+                        let plc_strongly_writeable = !self.check_place_below_reference(plc);
+                        let (expr_annot, pre_stmt_annots, post_stmt_annots) =
+                            regions::assignment::get_assignment_annots(
+                                self.env, &mut self.inclusion_tracker, &self.ty_translator,
+                                loc, plc_strongly_writeable, plc_ty, rhs_ty);
+
+                        // TODO; maybe move this to rvalue
+                        let composite_annots = regions::composite::get_composite_rvalue_creation_annots(
+                            self.env, &mut self.inclusion_tracker, &self.ty_translator, loc, rhs_ty);
+
+                        cont_stmt = radium::Stmt::with_annotations(
+                            cont_stmt,
+                            post_stmt_annots,
+                            &Some("post-assignment".to_owned()),
+                        );
+
+                        let translated_val = radium::Expr::with_optional_annotation(
+                            self.translate_rvalue(loc, val)?,
+                            expr_annot,
+                            Some("assignment".to_owned()),
+                        );
+                        let translated_place = self.translate_place(plc)?;
+                        let synty = self.ty_translator.translate_type_to_syn_type(plc_ty.ty)?;
+                        cont_stmt = radium::Stmt::Assign {
+                            ot: synty.into(),
+                            e1: translated_place,
+                            e2: translated_val,
+                            s: Box::new(cont_stmt),
+                        };
+                        cont_stmt = radium::Stmt::with_annotations(
+                            cont_stmt,
+                            pre_stmt_annots,
+                            &Some("assignment".to_owned()),
+                        );
+                        cont_stmt = radium::Stmt::with_annotations(
+                            cont_stmt,
+                            borrow_annots,
+                            &Some("borrow".to_owned()),
+                        );
+                        cont_stmt = radium::Stmt::with_annotations(
+                            cont_stmt,
+                            composite_annots,
+                            &Some("composite".to_owned()),
+                        );
+                    }
+                },
+
+                StatementKind::Deinit(_) => {
+                    // TODO: find out where this is emitted
+                    return Err(TranslationError::UnsupportedFeature {
+                        description: "RefinedRust does currently not support Deinit".to_owned(),
+                    });
+                },
+
+                StatementKind::FakeRead(b) => {
+                    // we can probably ignore this, but I'm not sure
+                    info!("Ignoring FakeRead: {:?}", b);
+                },
+
+                StatementKind::Intrinsic(intrinsic) => {
+                    match intrinsic.as_ref() {
+                        NonDivergingIntrinsic::Assume(_) => {
+                            // ignore
+                            info!("Ignoring Assume: {:?}", intrinsic);
+                        },
+                        NonDivergingIntrinsic::CopyNonOverlapping(_) => {
+                            return Err(TranslationError::UnsupportedFeature {
+                                description: "RefinedRust does currently not support the CopyNonOverlapping Intrinsic".to_owned(),
+                            });
+                        },
+                    }
+                },
+
+                StatementKind::PlaceMention(place) => {
+                    // TODO: this is missed UB
+                    info!("Ignoring PlaceMention: {:?}", place);
+                },
+
+                StatementKind::SetDiscriminant {
+                    place: _place,
+                    variant_index: _variant_index,
+                } => {
+                    // TODO
+                    return Err(TranslationError::UnsupportedFeature {
+                        description: "RefinedRust does currently not support SetDiscriminant".to_owned(),
+                    });
+                },
+
+                // don't need that info
+                | StatementKind::AscribeUserType(_, _)
+                // don't need that
+                | StatementKind::Coverage(_)
+                // no-op
+                | StatementKind::ConstEvalCounter
+                // ignore
+                | StatementKind::Nop
+                // just ignore
+                | StatementKind::StorageLive(_)
+                // just ignore
+                | StatementKind::StorageDead(_)
+                // just ignore retags
+                | StatementKind::Retag(_, _) => (),
+            }
+
+            cont_stmt = self.prepend_endlfts(cont_stmt, dying_loans.into_iter());
+        }
+
+        Ok(cont_stmt)
+    }
+
+    /// Get predecessors in the CFG.
+    fn get_loc_predecessors(&self, loc: Location) -> Vec<Location> {
+        if loc.statement_index > 0 {
+            let pred = Location {
+                block: loc.block,
+                statement_index: loc.statement_index - 1,
+            };
+            vec![pred]
+        } else {
+            // check for gotos that go to this basic block
+            let pred_bbs = self.proc.predecessors(loc.block);
+            let basic_blocks = &self.proc.get_mir().basic_blocks;
+            pred_bbs
+                .iter()
+                .map(|bb| {
+                    let data = &basic_blocks[*bb];
+                    Location {
+                        block: *bb,
+                        statement_index: data.statements.len(),
+                    }
+                })
+                .collect()
+        }
+    }
+}
diff --git a/rr_frontend/translation/src/body/translation/calls.rs b/rr_frontend/translation/src/body/translation/calls.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3be93470555480ac58dc92392d2c5e212cef6b3a
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/calls.rs
@@ -0,0 +1,626 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use super::TX;
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+/// Get the syntypes of function arguments for a procedure call.
+fn get_arg_syntypes_for_procedure_call<'tcx, 'def>(
+    tcx: ty::TyCtxt<'tcx>,
+    ty_translator: &types::LocalTX<'def, 'tcx>,
+    callee_did: DefId,
+    ty_params: &[ty::GenericArg<'tcx>],
+) -> Result<Vec<radium::SynType>, TranslationError<'tcx>> {
+    let caller_did = ty_translator.get_proc_did();
+
+    // Get the type of the callee, fully instantiated
+    let full_ty: ty::EarlyBinder<Ty<'tcx>> = tcx.type_of(callee_did);
+    let full_ty = full_ty.instantiate(tcx, ty_params);
+
+    // We create a dummy scope in order to make the lifetime lookups succeed, since we only want
+    // syntactic types here.
+    // Since we do the substitution of the generics above, we should translate generics and
+    // traits in the caller's scope.
+    let scope = ty_translator.scope.borrow();
+    let param_env: ty::ParamEnv<'tcx> = tcx.param_env(scope.did);
+    let callee_state = types::CalleeState::new(&param_env, &scope.generic_scope);
+    let mut dummy_state = types::STInner::CalleeTranslation(callee_state);
+
+    let mut syntypes = Vec::new();
+    match full_ty.kind() {
+        ty::TyKind::FnDef(_, _) => {
+            let sig = full_ty.fn_sig(tcx);
+            for ty in sig.inputs().skip_binder() {
+                let st = ty_translator.translator.translate_type_to_syn_type(*ty, &mut dummy_state)?;
+                syntypes.push(st);
+            }
+        },
+        ty::TyKind::Closure(_, args) => {
+            let clos_args = args.as_closure();
+            let sig = clos_args.sig();
+            let pre_sig = sig.skip_binder();
+            // we also need to add the closure argument here
+
+            let tuple_ty = clos_args.tupled_upvars_ty();
+            match clos_args.kind() {
+                ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
+                    syntypes.push(radium::SynType::Ptr);
+                },
+                ty::ClosureKind::FnOnce => {
+                    let st =
+                        ty_translator.translator.translate_type_to_syn_type(tuple_ty, &mut dummy_state)?;
+                    syntypes.push(st);
+                },
+            }
+            for ty in pre_sig.inputs() {
+                let st = ty_translator.translator.translate_type_to_syn_type(*ty, &mut dummy_state)?;
+                syntypes.push(st);
+            }
+        },
+        _ => unimplemented!(),
+    }
+
+    Ok(syntypes)
+}
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Internally register that we have used a procedure with a particular instantiation of generics, and
+    /// return the code parameter name.
+    /// Arguments:
+    /// - `callee_did`: the `DefId` of the callee
+    /// - `ty_params`: the instantiation for the callee's type parameters
+    /// - `trait_spec_terms`: if the callee has any trait assumptions, these are specification parameter terms
+    ///   for these traits
+    /// - `trait_assoc_tys`: if the callee has any trait assumptions, these are the instantiations for all
+    ///   associated types
+    fn register_use_procedure(
+        &mut self,
+        callee_did: DefId,
+        extra_spec_args: Vec<String>,
+        ty_params: ty::GenericArgsRef<'tcx>,
+        trait_specs: Vec<radium::TraitReqInst<'def, ty::Ty<'tcx>>>,
+    ) -> Result<(String, Vec<radium::Type<'def>>, Vec<radium::Lft>), TranslationError<'tcx>> {
+        trace!("enter register_use_procedure callee_did={callee_did:?} ty_params={ty_params:?}");
+        // The key does not include the associated types, as the resolution of the associated types
+        // should always be unique for one combination of type parameters, as long as we remain in
+        // the same environment (which is the case within this procedure).
+        // Therefore, already the type parameters are sufficient to distinguish different
+        // instantiations.
+        let key = types::generate_args_inst_key(self.env.tcx(), ty_params)?;
+
+        // re-quantify
+        let quantified_args = self.ty_translator.get_generic_abstraction_for_procedure(
+            callee_did,
+            ty_params,
+            &trait_specs,
+            true,
+        )?;
+
+        let tup = (callee_did, key);
+        let res;
+        if let Some(proc_use) = self.collected_procedures.get(&tup) {
+            res = proc_use.loc_name.clone();
+        } else {
+            let meta = self
+                .procedure_registry
+                .lookup_function(callee_did)
+                .ok_or_else(|| TranslationError::UnknownProcedure(format!("{:?}", callee_did)))?;
+            // explicit instantiation is needed sometimes
+            let spec_name = format!("{} (RRGS:=RRGS)", meta.get_spec_name());
+            let code_name = meta.get_name();
+            let loc_name = format!("{}_loc", types::mangle_name_with_tys(code_name, tup.1.as_slice()));
+
+            let syntypes = get_arg_syntypes_for_procedure_call(
+                self.env.tcx(),
+                &self.ty_translator,
+                callee_did,
+                ty_params.as_slice(),
+            )?;
+
+            let mut translated_params = quantified_args.fn_ty_param_inst;
+
+            info!(
+                "Registered procedure instance {} of {:?} with {:?} and layouts {:?}",
+                loc_name, callee_did, translated_params, syntypes
+            );
+
+            let specs = trait_specs.into_iter().map(|x| x.try_into().unwrap()).collect();
+
+            let proc_use = radium::UsedProcedure::new(
+                loc_name,
+                spec_name,
+                extra_spec_args,
+                quantified_args.scope,
+                specs,
+                translated_params,
+                quantified_args.fn_lft_param_inst,
+                syntypes,
+            );
+
+            res = proc_use.loc_name.clone();
+            self.collected_procedures.insert(tup, proc_use);
+        }
+        trace!("leave register_use_procedure");
+        Ok((res, quantified_args.callee_ty_param_inst, quantified_args.callee_lft_param_inst))
+    }
+
+    /// Internally register that we have used a trait method with a particular instantiation of
+    /// generics, and return the code parameter name.
+    fn register_use_trait_method<'c>(
+        &'c mut self,
+        callee_did: DefId,
+        ty_params: ty::GenericArgsRef<'tcx>,
+        trait_specs: Vec<radium::TraitReqInst<'def, ty::Ty<'tcx>>>,
+    ) -> Result<(String, Vec<radium::Type<'def>>, Vec<radium::Lft>), TranslationError<'tcx>> {
+        trace!("enter register_use_trait_method did={:?} ty_params={:?}", callee_did, ty_params);
+        // Does not include the associated types in the key; see `register_use_procedure` for an
+        // explanation.
+        let key = types::generate_args_inst_key(self.env.tcx(), ty_params)?;
+
+        let (method_loc_name, method_spec_term, method_params) =
+            self.ty_translator.register_use_trait_procedure(self.env, callee_did, ty_params)?;
+        // re-quantify
+        let quantified_args = self.ty_translator.get_generic_abstraction_for_procedure(
+            callee_did,
+            method_params,
+            &trait_specs,
+            false,
+        )?;
+
+        let tup = (callee_did, key);
+        let res;
+        if let Some(proc_use) = self.collected_procedures.get(&tup) {
+            res = proc_use.loc_name.clone();
+        } else {
+            // TODO: should we use ty_params or method_params?
+            let syntypes = get_arg_syntypes_for_procedure_call(
+                self.env.tcx(),
+                &self.ty_translator,
+                callee_did,
+                ty_params.as_slice(),
+            )?;
+
+            let mut translated_params = quantified_args.fn_ty_param_inst;
+
+            info!(
+                "Registered procedure instance {} of {:?} with {:?} and layouts {:?}",
+                method_loc_name, callee_did, translated_params, syntypes
+            );
+
+            let specs = trait_specs.into_iter().map(|x| x.try_into().unwrap()).collect();
+            let proc_use = radium::UsedProcedure::new(
+                method_loc_name,
+                method_spec_term,
+                vec![],
+                quantified_args.scope,
+                specs,
+                translated_params,
+                quantified_args.fn_lft_param_inst,
+                syntypes,
+            );
+
+            res = proc_use.loc_name.clone();
+            self.collected_procedures.insert(tup, proc_use);
+        }
+        trace!("leave register_use_procedure");
+        Ok((res, quantified_args.callee_ty_param_inst, quantified_args.callee_lft_param_inst))
+    }
+
+    /// Resolve the trait requirements of a function call.
+    /// The target of the call, [did], should have been resolved as much as possible,
+    /// as the requirements of a call can be different depending on which impl we consider.
+    fn resolve_trait_requirements_of_call(
+        &self,
+        did: DefId,
+        params: ty::GenericArgsRef<'tcx>,
+    ) -> Result<Vec<radium::TraitReqInst<'def, Ty<'tcx>>>, TranslationError<'tcx>> {
+        let mut scope = self.ty_translator.scope.borrow_mut();
+        let mut state = types::STInner::InFunction(&mut scope);
+        self.trait_registry.resolve_trait_requirements_in_state(&mut state, did, params)
+    }
+
+    /// Translate the use of an `FnDef`, registering that the current function needs to link against
+    /// a particular monomorphization of the used function.
+    /// Is guaranteed to return a `radium::Expr::CallTarget` with the parameter instantiation of
+    /// this function annotated.
+    pub(super) fn translate_fn_def_use(
+        &mut self,
+        ty: Ty<'tcx>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        let TyKind::FnDef(defid, params) = ty.kind() else {
+            return Err(TranslationError::UnknownError("not a FnDef type".to_owned()));
+        };
+
+        let current_param_env: ty::ParamEnv<'tcx> = self.env.tcx().param_env(self.proc.get_id());
+
+        // Check whether we are calling into a trait method.
+        // This works since we did not resolve concrete instances, so this is always an abstract
+        // reference to the trait.
+        let calling_trait = self.env.tcx().trait_of_item(*defid);
+
+        // Check whether we are calling a plain function or a trait method
+        let Some(calling_trait) = calling_trait else {
+            // resolve the trait requirements
+            let trait_spec_terms = self.resolve_trait_requirements_of_call(*defid, params)?;
+
+            // track that we are using this function and generate the Coq location name
+            let (code_param_name, ty_hint, lft_hint) =
+                self.register_use_procedure(*defid, vec![], params, trait_spec_terms)?;
+
+            let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
+
+            return Ok(radium::Expr::CallTarget(code_param_name, ty_hint, lft_hint));
+        };
+
+        // Otherwise, we are calling a trait method
+        // Resolve the trait instance using trait selection
+        let Some((resolved_did, resolved_params, kind)) =
+            resolution::resolve_assoc_item(self.env.tcx(), current_param_env, *defid, params)
+        else {
+            return Err(TranslationError::TraitResolution(format!("Could not resolve trait {:?}", defid)));
+        };
+
+        info!(
+            "Resolved trait method {:?} as {:?} with substs {:?} and kind {:?}",
+            defid, resolved_did, resolved_params, kind
+        );
+
+        match kind {
+            resolution::TraitResolutionKind::UserDefined => {
+                // We can statically resolve the particular trait instance,
+                // but need to apply the spec to the instance's spec attributes
+
+                // resolve the trait requirements
+                let trait_spec_terms =
+                    self.resolve_trait_requirements_of_call(resolved_did, resolved_params)?;
+
+                let (param_name, ty_hint, lft_hint) =
+                    self.register_use_procedure(resolved_did, vec![], resolved_params, trait_spec_terms)?;
+                let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
+
+                Ok(radium::Expr::CallTarget(param_name, ty_hint, lft_hint))
+            },
+
+            resolution::TraitResolutionKind::Param => {
+                // In this case, we have already applied it to the spec attribute
+
+                // resolve the trait requirements
+                let trait_spec_terms = self.resolve_trait_requirements_of_call(*defid, params)?;
+
+                let (param_name, ty_hint, lft_hint) =
+                    self.register_use_trait_method(resolved_did, resolved_params, trait_spec_terms)?;
+                let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
+
+                Ok(radium::Expr::CallTarget(param_name, ty_hint, lft_hint))
+            },
+
+            resolution::TraitResolutionKind::Closure => {
+                // TODO: here, we should first generate an instance of the trait
+                //let body = self.env.tcx().instance_mir(middle::ty::InstanceDef::Item(resolved_did));
+                //let body = self.env.tcx().instance_mir(middle::ty::InstanceDef::FnPtrShim(*defid, ty));
+                //info!("closure body: {:?}", body);
+
+                //FunctionTranslator::dump_body(body);
+
+                //let res_result = ty::Instance::resolve(self.env.tcx(), callee_param_env, *defid, params);
+                //info!("Resolution {:?}", res_result);
+
+                // the args are just the closure args. We can ignore them.
+                let _clos_args = resolved_params.as_closure();
+
+                // resolve the trait requirements
+                let trait_spec_terms = self.resolve_trait_requirements_of_call(*defid, params)?;
+
+                let (param_name, ty_hint, lft_hint) =
+                    self.register_use_procedure(resolved_did, vec![], ty::List::empty(), trait_spec_terms)?;
+                let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
+
+                Ok(radium::Expr::CallTarget(param_name, ty_hint, lft_hint))
+            },
+        }
+    }
+
+    /// Split the type of a function operand of a call expression to a base type and an instantiation for
+    /// generics.
+    fn call_expr_op_split_inst(
+        &self,
+        constant: &Constant<'tcx>,
+    ) -> Result<
+        (DefId, ty::PolyFnSig<'tcx>, ty::GenericArgsRef<'tcx>, ty::PolyFnSig<'tcx>),
+        TranslationError<'tcx>,
+    > {
+        match constant.literal {
+            ConstantKind::Ty(c) => {
+                match c.ty().kind() {
+                    TyKind::FnDef(def, args) => {
+                        let ty: ty::EarlyBinder<Ty<'tcx>> = self.env.tcx().type_of(def);
+                        let ty_ident = ty.instantiate_identity();
+                        assert!(ty_ident.is_fn());
+                        let ident_sig = ty_ident.fn_sig(self.env.tcx());
+
+                        let ty_instantiated = ty.instantiate(self.env.tcx(), args.as_slice());
+                        let instantiated_sig = ty_instantiated.fn_sig(self.env.tcx());
+
+                        Ok((*def, ident_sig, args, instantiated_sig))
+                    },
+                    // TODO handle FnPtr, closure
+                    _ => Err(TranslationError::Unimplemented {
+                        description: "implement function pointers".to_owned(),
+                    }),
+                }
+            },
+            ConstantKind::Val(_, ty) => {
+                match ty.kind() {
+                    TyKind::FnDef(def, args) => {
+                        let ty: ty::EarlyBinder<Ty<'tcx>> = self.env.tcx().type_of(def);
+
+                        let ty_ident = ty.instantiate_identity();
+                        assert!(ty_ident.is_fn());
+                        let ident_sig = ty_ident.fn_sig(self.env.tcx());
+
+                        let ty_instantiated = ty.instantiate(self.env.tcx(), args.as_slice());
+                        let instantiated_sig = ty_instantiated.fn_sig(self.env.tcx());
+
+                        Ok((*def, ident_sig, args, instantiated_sig))
+                    },
+                    // TODO handle FnPtr, closure
+                    _ => Err(TranslationError::Unimplemented {
+                        description: "implement function pointers".to_owned(),
+                    }),
+                }
+            },
+            ConstantKind::Unevaluated(_, _) => Err(TranslationError::Unimplemented {
+                description: "implement ConstantKind::Unevaluated".to_owned(),
+            }),
+        }
+    }
+
+    pub(super) fn translate_function_call(
+        &mut self,
+        func: &Operand<'tcx>,
+        args: &[Operand<'tcx>],
+        destination: &Place<'tcx>,
+        target: Option<middle::mir::BasicBlock>,
+        loc: Location,
+        dying_loans: &[facts::Loan],
+    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
+        let startpoint = self.info.interner.get_point_index(&facts::Point {
+            location: loc,
+            typ: facts::PointType::Start,
+        });
+
+        let Operand::Constant(box func_constant) = func else {
+            return Err(TranslationError::UnsupportedFeature {
+                description: format!(
+                    "RefinedRust does currently not support this kind of call operand (got: {:?})",
+                    func
+                ),
+            });
+        };
+
+        // Get the type of the return value from the function
+        let (target_did, sig, generic_args, inst_sig) = self.call_expr_op_split_inst(func_constant)?;
+        info!("calling function {:?}", target_did);
+        info!("call substs: {:?} = {:?}, {:?}", func, sig, generic_args);
+
+        // for lifetime annotations:
+        // 1. get the regions involved here. for that, get the instantiation of the function.
+        //    + if it's a FnDef type, that should be easy.
+        //    + for a function pointer: ?
+        //    + for a closure: ?
+        //   (Polonius does not seem to distinguish early/late bound in any way, except
+        //   that they are numbered in different passes)
+        // 2. find the constraints here involving the regions.
+        // 3. solve for the regions.
+        //    + transitively propagate the constraints
+        //    + check for equalities
+        //    + otherwise compute intersection. singleton intersection just becomes an equality def.
+        // 4. add annotations accordingly
+        //    + either a startlft
+        //    + or a copy name
+        // 5. add shortenlft annotations to line up arguments.
+        //    + for that, we need the type of the LHS, and what the argument types (with
+        //    substituted regions) should be.
+        // 6. annotate the return value on assignment and establish constraints.
+
+        let classification =
+            regions::calls::compute_call_regions(self.env, &self.inclusion_tracker, generic_args, loc);
+
+        // update the inclusion tracker with the new regions we have introduced
+        // We just add the inclusions and ignore that we resolve it in a "tight" way.
+        // the cases where we need the reverse inclusion should be really rare.
+        for (r, c) in &classification.classification {
+            match c {
+                regions::calls::CallRegionKind::EqR(r2) => {
+                    // put it at the start point, because the inclusions come into effect
+                    // at the point right before.
+                    self.inclusion_tracker.add_static_inclusion(*r, *r2, startpoint);
+                    self.inclusion_tracker.add_static_inclusion(*r2, *r, startpoint);
+                },
+                regions::calls::CallRegionKind::Intersection(lfts) => {
+                    // all the regions represented by lfts need to be included in r
+                    for r2 in lfts {
+                        self.inclusion_tracker.add_static_inclusion(*r2, *r, startpoint);
+                    }
+                },
+            }
+        }
+
+        // translate the function expression.
+        let func_expr = self.translate_operand(func, false)?;
+        // We expect this to be an Expr::CallTarget, being annotated with the type parameters we
+        // instantiate it with.
+        let radium::Expr::CallTarget(func_lit, ty_param_annots, mut lft_param_annots) = func_expr else {
+            unreachable!("Logic error in call target translation");
+        };
+        let func_expr = radium::Expr::MetaParam(func_lit);
+
+        // translate the arguments
+        let mut translated_args = Vec::new();
+        for arg in args {
+            // to_ty is the type the function expects
+
+            //let ty = arg.ty(&self.proc.get_mir().local_decls, self.env.tcx());
+            let translated_arg = self.translate_operand(arg, true)?;
+            translated_args.push(translated_arg);
+        }
+
+        // We have to add the late regions, since we do not requantify over them.
+        for late in &classification.late_regions {
+            let lft = self.format_region(*late);
+            lft_param_annots.push(lft);
+        }
+        info!("Call lifetime instantiation (early): {:?}", classification.early_regions);
+        info!("Call lifetime instantiation (late): {:?}", classification.late_regions);
+
+        // TODO: do we need to do something with late bounds?
+        let output_ty = inst_sig.output().skip_binder();
+        info!("call has instantiated type {:?}", inst_sig);
+
+        // compute the resulting annotations
+        let lhs_ty = self.get_type_of_place(destination);
+        let lhs_strongly_writeable = !self.check_place_below_reference(destination);
+        let (rhs_annots, pre_stmt_annots, post_stmt_annots) = regions::assignment::get_assignment_annots(
+            self.env,
+            &mut self.inclusion_tracker,
+            &self.ty_translator,
+            loc,
+            lhs_strongly_writeable,
+            lhs_ty,
+            output_ty,
+        );
+        info!(
+            "assignment annots after call: expr: {:?}, pre-stmt: {:?}, post-stmt: {:?}",
+            rhs_annots, pre_stmt_annots, post_stmt_annots
+        );
+
+        // TODO: add annotations for the assignment
+        // for that:
+        // - get the type of the place
+        // - enforce constraints as necessary. this might spawn dyninclusions with some of the new regions =>
+        //   In Coq, also the aliases should get proper endlft events to resolve the dyninclusions.
+        // - update the name map
+        let call_expr = radium::Expr::Call {
+            f: Box::new(func_expr),
+            lfts: lft_param_annots,
+            tys: ty_param_annots,
+            args: translated_args,
+        };
+        let stmt = match target {
+            Some(target) => {
+                let mut cont_stmt = self.translate_goto_like(&loc, target)?;
+                // end loans before the goto, but after the call.
+                // TODO: may cause duplications?
+                cont_stmt = self.prepend_endlfts(cont_stmt, dying_loans.iter().copied());
+
+                let cont_stmt = radium::Stmt::with_annotations(
+                    cont_stmt,
+                    post_stmt_annots,
+                    &Some("post_function_call".to_owned()),
+                );
+
+                // assign stmt with call; then jump to bb
+                let place_ty = self.get_type_of_place(destination);
+                let place_st = self.ty_translator.translate_type_to_syn_type(place_ty.ty)?;
+                let place_expr = self.translate_place(destination)?;
+                let ot = place_st.into();
+
+                let annotated_rhs = radium::Expr::with_optional_annotation(
+                    call_expr,
+                    rhs_annots,
+                    Some("function_call".to_owned()),
+                );
+                let assign_stmt = radium::Stmt::Assign {
+                    ot,
+                    e1: place_expr,
+                    e2: annotated_rhs,
+                    s: Box::new(cont_stmt),
+                };
+                radium::Stmt::with_annotations(
+                    assign_stmt,
+                    pre_stmt_annots,
+                    &Some("function_call".to_owned()),
+                )
+            },
+            None => {
+                // expr stmt with call; then stuck (we have not provided a continuation, after all)
+                radium::Stmt::ExprS {
+                    e: call_expr,
+                    s: Box::new(radium::Stmt::Stuck),
+                }
+            },
+        };
+
+        let mut stmt_annots = Vec::new();
+
+        // add annotations to initialize the regions for the call (before the call)
+        for (r, class) in &classification.classification {
+            let lft = self.format_region(*r);
+            match class {
+                regions::calls::CallRegionKind::EqR(r2) => {
+                    let lft2 = self.format_region(*r2);
+                    stmt_annots.push(radium::Annotation::CopyLftName(lft2, lft));
+                },
+
+                regions::calls::CallRegionKind::Intersection(rs) => {
+                    match rs.len() {
+                        0 => {
+                            return Err(TranslationError::UnsupportedFeature {
+                                description: "RefinedRust does currently not support unconstrained lifetime"
+                                    .to_owned(),
+                            });
+                        },
+                        1 => {
+                            // this is really just an equality constraint
+                            if let Some(r2) = rs.iter().next() {
+                                let lft2 = self.format_region(*r2);
+                                stmt_annots.push(radium::Annotation::CopyLftName(lft2, lft));
+                            }
+                        },
+                        _ => {
+                            // a proper intersection
+                            let lfts: Vec<_> = rs.iter().map(|r| self.format_region(*r)).collect();
+                            stmt_annots.push(radium::Annotation::AliasLftIntersection(lft, lfts));
+                        },
+                    };
+                },
+            }
+        }
+
+        let stmt = radium::Stmt::with_annotations(stmt, stmt_annots, &Some("function_call".to_owned()));
+        Ok(stmt)
+    }
+}
diff --git a/rr_frontend/translation/src/body/translation/constants.rs b/rr_frontend/translation/src/body/translation/constants.rs
new file mode 100644
index 0000000000000000000000000000000000000000..34e616362265b033316feb4833d01814bcce08df
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/constants.rs
@@ -0,0 +1,211 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use super::TX;
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Translate a scalar at a specific type to a `radium::Expr`.
+    // TODO: Use `TryFrom` instead
+    fn translate_scalar(
+        &mut self,
+        sc: &Scalar,
+        ty: Ty<'tcx>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        // TODO: Use `TryFrom` instead
+        fn translate_literal<'tcx, T, U>(
+            sc: Result<T, U>,
+            fptr: fn(T) -> radium::Literal,
+        ) -> Result<radium::Expr, TranslationError<'tcx>> {
+            sc.map_or(Err(TranslationError::InvalidLayout), |lit| Ok(radium::Expr::Literal(fptr(lit))))
+        }
+
+        match ty.kind() {
+            TyKind::Bool => translate_literal(sc.to_bool(), radium::Literal::Bool),
+
+            TyKind::Int(it) => match it {
+                ty::IntTy::I8 => translate_literal(sc.to_i8(), radium::Literal::I8),
+                ty::IntTy::I16 => translate_literal(sc.to_i16(), radium::Literal::I16),
+                ty::IntTy::I32 => translate_literal(sc.to_i32(), radium::Literal::I32),
+                ty::IntTy::I128 => translate_literal(sc.to_i128(), radium::Literal::I128),
+
+                // For Radium, the pointer size is 8 bytes
+                ty::IntTy::I64 | ty::IntTy::Isize => translate_literal(sc.to_i64(), radium::Literal::I64),
+            },
+
+            TyKind::Uint(it) => match it {
+                ty::UintTy::U8 => translate_literal(sc.to_u8(), radium::Literal::U8),
+                ty::UintTy::U16 => translate_literal(sc.to_u16(), radium::Literal::U16),
+                ty::UintTy::U32 => translate_literal(sc.to_u32(), radium::Literal::U32),
+                ty::UintTy::U128 => translate_literal(sc.to_u128(), radium::Literal::U128),
+
+                // For Radium, the pointer is 8 bytes
+                ty::UintTy::U64 | ty::UintTy::Usize => translate_literal(sc.to_u64(), radium::Literal::U64),
+            },
+
+            TyKind::Char => translate_literal(sc.to_char(), radium::Literal::Char),
+
+            TyKind::FnDef(_, _) => self.translate_fn_def_use(ty),
+
+            TyKind::Tuple(tys) => {
+                if tys.is_empty() {
+                    return Ok(radium::Expr::Literal(radium::Literal::ZST));
+                }
+
+                Err(TranslationError::UnsupportedFeature {
+                    description: format!(
+                        "RefinedRust does currently not support compound construction of tuples using literals (got: {:?})",
+                        ty
+                    ),
+                })
+            },
+
+            TyKind::Ref(_, _, _) => match sc {
+                Scalar::Int(_) => unreachable!(),
+
+                Scalar::Ptr(pointer, _) => {
+                    let glob_alloc = self.env.tcx().global_alloc(pointer.provenance);
+                    match glob_alloc {
+                        middle::mir::interpret::GlobalAlloc::Static(did) => {
+                            info!(
+                                "Found static GlobalAlloc {:?} for Ref scalar {:?} at type {:?}",
+                                did, sc, ty
+                            );
+
+                            let s = self.const_registry.get_static(did)?;
+                            self.collected_statics.insert(did);
+                            Ok(radium::Expr::Literal(radium::Literal::Loc(s.loc_name.clone())))
+                        },
+                        middle::mir::interpret::GlobalAlloc::Memory(alloc) => {
+                            // TODO: this is needed
+                            Err(TranslationError::UnsupportedFeature {
+                                description: format!(
+                                    "RefinedRust does currently not support GlobalAlloc {:?} for scalar {:?} at type {:?}",
+                                    glob_alloc, sc, ty
+                                ),
+                            })
+                        },
+                        _ => Err(TranslationError::UnsupportedFeature {
+                            description: format!(
+                                "RefinedRust does currently not support GlobalAlloc {:?} for scalar {:?} at type {:?}",
+                                glob_alloc, sc, ty
+                            ),
+                        }),
+                    }
+                },
+            },
+
+            _ => Err(TranslationError::UnsupportedFeature {
+                description: format!(
+                    "RefinedRust does currently not support layout for const value: (got: {:?})",
+                    ty
+                ),
+            }),
+        }
+    }
+
+    /// Translate a constant value from const evaluation.
+    fn translate_constant_value(
+        &mut self,
+        v: mir::interpret::ConstValue<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        match v {
+            ConstValue::Scalar(sc) => self.translate_scalar(&sc, ty),
+            ConstValue::ZeroSized => {
+                // TODO are there more special cases we need to handle somehow?
+                match ty.kind() {
+                    TyKind::FnDef(_, _) => {
+                        info!("Translating ZST val for function call target: {:?}", ty);
+                        self.translate_fn_def_use(ty)
+                    },
+                    _ => Ok(radium::Expr::Literal(radium::Literal::ZST)),
+                }
+            },
+            _ => {
+                // TODO: do we actually care about this case or is this just something that can
+                // appear as part of CTFE/MIRI?
+                Err(TranslationError::UnsupportedFeature {
+                    description: format!("Unsupported Constant: ConstValue; {:?}", v),
+                })
+            },
+        }
+    }
+
+    /// Translate a Constant to a `radium::Expr`.
+    pub(super) fn translate_constant(
+        &mut self,
+        constant: &Constant<'tcx>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        match constant.literal {
+            ConstantKind::Ty(v) => {
+                let const_ty = v.ty();
+
+                match v.kind() {
+                    ConstKind::Value(v) => {
+                        // this doesn't contain the necessary structure anymore. Need to reconstruct using the
+                        // type.
+                        match v.try_to_scalar() {
+                            Some(sc) => self.translate_scalar(&sc, const_ty),
+                            _ => Err(TranslationError::UnsupportedFeature {
+                                description: format!("const value not supported: {:?}", v),
+                            }),
+                        }
+                    },
+                    _ => Err(TranslationError::UnsupportedFeature {
+                        description: "Unsupported ConstKind".to_owned(),
+                    }),
+                }
+            },
+            ConstantKind::Val(val, ty) => self.translate_constant_value(val, ty),
+            ConstantKind::Unevaluated(c, ty) => {
+                // call const evaluation
+                let param_env: ty::ParamEnv<'tcx> = self.env.tcx().param_env(self.proc.get_id());
+                match self.env.tcx().const_eval_resolve(param_env, c, None) {
+                    Ok(res) => self.translate_constant_value(res, ty),
+                    Err(e) => match e {
+                        ErrorHandled::Reported(_) => Err(TranslationError::UnsupportedFeature {
+                            description: "Cannot interpret constant".to_owned(),
+                        }),
+                        ErrorHandled::TooGeneric => Err(TranslationError::UnsupportedFeature {
+                            description: "Const use is too generic".to_owned(),
+                        }),
+                    },
+                }
+            },
+        }
+    }
+}
diff --git a/rr_frontend/translation/src/body/translation/loops.rs b/rr_frontend/translation/src/body/translation/loops.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6f8ef41f6d78cc3fb695c0ad3c94b1308dbc640c
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/loops.rs
@@ -0,0 +1,137 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use super::TX;
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Parse the attributes on spec closure `did` as loop annotations and add it as an invariant
+    /// to the generated code.
+    pub(super) fn parse_attributes_on_loop_spec_closure(
+        &self,
+        loop_head: BasicBlock,
+        did: Option<DefId>,
+    ) -> radium::LoopSpec {
+        // for now: just make invariants True.
+
+        // need to do:
+        // - find out the locals in the right order, make parameter names for them. based on their type and
+        //   initialization status, get the refinement type.
+        // - output/pretty-print this map when generating the typing proof of each function. [done]
+        //  + should not be a separate definition, but rather a "set (.. := ...)" with a marker type so
+        //    automation can find it.
+
+        // representation of loop invariants:
+        // - introduce parameters for them.
+
+        let mut rfn_binders = Vec::new();
+        let prop_body = radium::IProp::True;
+
+        // determine invariant on initialization:
+        // - we need this both for the refinement invariant (though this could be removed if we make uninit
+        //   generic over the refinement)
+        // - in order to establish the initialization invariant in each loop iteration, since we don't have
+        //   proper subtyping for uninit => maybe we could fix this too by making uninit variant in the
+        //   refinement type? then we could have proper subtyping lemmas.
+        //  + to bring it to the right refinement type initially, maybe have some automation /
+        //  annotation
+        // TODO: consider changing it like that.
+        //
+        // Note that StorageDead will not help us for determining initialization/ making it invariant, since
+        // it only applies to full stack slots, not individual paths. one thing that makes it more
+        // complicated in the frontend: initialization may in practice also be path-dependent.
+        //  - this does not cause issues with posing a too strong loop invariant,
+        //  - but this poses an issue for annotations
+        //
+        //
+
+        // get locals
+        for (_, name, ty) in &self.fn_locals {
+            // get the refinement type
+            let mut rfn_ty = ty.get_rfn_type();
+            // wrap it in place_rfn, since we reason about places
+            rfn_ty = coq::term::Type::PlaceRfn(Box::new(rfn_ty));
+
+            // determine their initialization status
+            //let initialized = true; // TODO
+            // determine the actual refinement type for the current initialization status.
+
+            let rfn_name = format!("r_{}", name);
+            rfn_binders.push(coq::binder::Binder::new(Some(rfn_name), rfn_ty));
+        }
+
+        // TODO what do we do about stuff connecting borrows?
+        if let Some(did) = did {
+            let attrs = self.env.get_attributes(did);
+            info!("attrs for loop {:?}: {:?}", loop_head, attrs);
+        } else {
+            info!("no attrs for loop {:?}", loop_head);
+        }
+
+        let pred = radium::IPropPredicate::new(rfn_binders, prop_body);
+        radium::LoopSpec {
+            func_predicate: pred,
+        }
+    }
+
+    /// Find the optional `DefId` of the closure giving the invariant for the loop with head `head_bb`.
+    pub(super) fn find_loop_spec_closure(
+        &self,
+        head_bb: BasicBlock,
+    ) -> Result<Option<DefId>, TranslationError<'tcx>> {
+        let bodies = self.proc.loop_info().get_loop_body(head_bb);
+        let basic_blocks = &self.proc.get_mir().basic_blocks;
+
+        // we go in order through the bodies in order to not stumble upon an annotation for a
+        // nested loop!
+        for body in bodies {
+            // check that we did not go below a nested loop
+            if self.proc.loop_info().get_loop_head(*body) == Some(head_bb) {
+                // check the statements for an assignment
+                let data = basic_blocks.get(*body).unwrap();
+                for stmt in &data.statements {
+                    if let StatementKind::Assign(box (pl, _)) = stmt.kind {
+                        if let Some(did) = self.is_spec_closure_local(pl.local)? {
+                            return Ok(Some(did));
+                        }
+                    }
+                }
+            }
+        }
+
+        Ok(None)
+    }
+}
diff --git a/rr_frontend/translation/src/body/translation/mod.rs b/rr_frontend/translation/src/body/translation/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5c3f407e2e31f247b403eb610f5287b014cb4b77
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/mod.rs
@@ -0,0 +1,498 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+mod basicblock;
+mod calls;
+mod constants;
+mod loops;
+mod place;
+mod rvalue;
+mod terminator;
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+/// Struct that keeps track of all information necessary to translate a MIR Body to a `radium::Function`.
+/// `'a` is the lifetime of the translator and ends after translation has finished.
+/// `'def` is the lifetime of the generated code (the code may refer to struct defs).
+/// `'tcx` is the lifetime of the rustc tctx.
+pub struct TX<'a, 'def, 'tcx> {
+    env: &'def Environment<'tcx>,
+    /// registry of other procedures
+    procedure_registry: &'a procedures::Scope<'def>,
+    /// scope of used consts
+    const_registry: &'a consts::Scope<'def>,
+    /// trait registry
+    trait_registry: &'def registry::TR<'tcx, 'def>,
+    /// translator for types
+    ty_translator: types::LocalTX<'def, 'tcx>,
+
+    /// this needs to be annotated with the right borrowck things
+    proc: &'def Procedure<'tcx>,
+    /// attributes on this function
+    attrs: &'a [&'a ast::ast::AttrItem],
+    /// polonius info for this function
+    info: &'a PoloniusInfo<'a, 'tcx>,
+
+    /// maps locals to variable names
+    variable_map: HashMap<Local, String>,
+
+    /// name of the return variable
+    return_name: String,
+    /// syntactic type of the thing to return
+    return_synty: radium::SynType,
+    /// all the other procedures used by this function, and:
+    /// (code_loc_parameter_name, spec_name, type_inst, syntype_of_all_args)
+    collected_procedures: HashMap<(DefId, types::GenericsKey<'tcx>), radium::UsedProcedure<'def>>,
+    /// used statics
+    collected_statics: HashSet<DefId>,
+
+    /// tracking lifetime inclusions for the generation of lifetime inclusions
+    inclusion_tracker: InclusionTracker<'a, 'tcx>,
+    /// initial Polonius constraints that hold at the start of the function
+    initial_constraints: Vec<(polonius_info::AtomicRegion, polonius_info::AtomicRegion)>,
+
+    /// local lifetimes: the LHS is the lifetime name, the RHS are the super lifetimes
+    local_lifetimes: Vec<(radium::specs::Lft, Vec<radium::specs::Lft>)>,
+    /// data structures for tracking which basic blocks still need to be translated
+    /// (we only translate the basic blocks which are actually reachable, in particular when
+    /// skipping unwinding)
+    bb_queue: Vec<BasicBlock>,
+    /// set of already processed blocks
+    processed_bbs: HashSet<BasicBlock>,
+
+    /// map of loop heads to their optional spec closure defid
+    loop_specs: HashMap<BasicBlock, Option<DefId>>,
+
+    /// relevant locals: (local, name, type)
+    fn_locals: Vec<(Local, String, radium::Type<'def>)>,
+
+    /// result temporaries of checked ops that we rewrite
+    /// we assume that this place is typed at (result_type, bool)
+    /// and rewrite accesses to the first component to directly use the place,
+    /// while rewriting accesses to the second component to true.
+    /// TODO: once we handle panics properly, we should use a different translation.
+    /// NOTE: we only rewrite for uses, as these are the only places these are used.
+    checked_op_temporaries: HashMap<Local, Ty<'tcx>>,
+
+    /// the Caesium function buildder
+    translated_fn: radium::FunctionBuilder<'def>,
+}
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    pub fn new(
+        env: &'def Environment<'tcx>,
+        procedure_registry: &'a procedures::Scope<'def>,
+        const_registry: &'a consts::Scope<'def>,
+        trait_registry: &'def registry::TR<'tcx, 'def>,
+        ty_translator: types::LocalTX<'def, 'tcx>,
+
+        proc: &'def Procedure<'tcx>,
+        attrs: &'a [&'a ast::ast::AttrItem],
+        info: &'a PoloniusInfo<'a, 'tcx>,
+        inputs: &[ty::Ty<'tcx>],
+
+        mut inclusion_tracker: InclusionTracker<'a, 'tcx>,
+        mut translated_fn: radium::FunctionBuilder<'def>,
+    ) -> Result<Self, TranslationError<'tcx>> {
+        let body = proc.get_mir();
+
+        // analyze which locals are used for the result of checked-ops, because we will
+        // override their types (eliminating the tuples)
+        let mut checked_op_analyzer = CheckedOpLocalAnalysis::new(env.tcx(), body);
+        checked_op_analyzer.analyze();
+        let checked_op_temporaries = checked_op_analyzer.results();
+
+        // map to translate between locals and the string names we use in radium::
+        let mut variable_map: HashMap<Local, String> = HashMap::new();
+
+        let local_decls = &body.local_decls;
+        info!("Have {} local decls\n", local_decls.len());
+
+        let debug_info = &body.var_debug_info;
+        info!("using debug info: {:?}", debug_info);
+
+        let mut return_synty = radium::SynType::Unit; // default
+        let mut fn_locals = Vec::new();
+        let mut opt_return_name =
+            Err(TranslationError::UnknownError("could not find local for return value".to_owned()));
+        let mut used_names = HashSet::new();
+        let mut arg_tys = Vec::new();
+
+        // go over local_decls and create the right radium:: stack layout
+        for (local, local_decl) in local_decls.iter_enumerated() {
+            let kind = body.local_kind(local);
+            let ty: Ty<'tcx>;
+            if let Some(rewritten_ty) = checked_op_temporaries.get(&local) {
+                ty = *rewritten_ty;
+            } else {
+                ty = local_decl.ty;
+            }
+
+            // check if the type is of a spec fn -- in this case, we can skip this temporary
+            if let TyKind::Closure(id, _) = ty.kind() {
+                if procedure_registry.lookup_function_mode(*id).map_or(false, procedures::Mode::is_ignore) {
+                    // this is a spec fn
+                    info!("skipping local which has specfn closure type: {:?}", local);
+                    continue;
+                }
+            }
+
+            // type:
+            info!("Trying to translate type of local {local:?}: {:?}", ty);
+            let tr_ty = ty_translator.translate_type(ty)?;
+            let st = (&tr_ty).into();
+
+            let name = Self::make_local_name(body, local, &mut used_names);
+            variable_map.insert(local, name.clone());
+
+            fn_locals.push((local, name.clone(), tr_ty));
+
+            match kind {
+                LocalKind::Arg => {
+                    translated_fn.code.add_argument(&name, st);
+                    arg_tys.push(ty);
+                },
+                //LocalKind::Var => translated_fn.code.add_local(&name, st),
+                LocalKind::Temp => translated_fn.code.add_local(&name, st),
+                LocalKind::ReturnPointer => {
+                    return_synty = st.clone();
+                    translated_fn.code.add_local(&name, st);
+                    opt_return_name = Ok(name);
+                },
+            }
+        }
+        info!("radium name map: {:?}", variable_map);
+        // create the function
+        let return_name = opt_return_name?;
+
+        // add lifetime parameters to the map
+        let initial_constraints = regions::init::get_initial_universal_arg_constraints(
+            info,
+            &mut inclusion_tracker,
+            inputs,
+            arg_tys.as_slice(),
+        );
+        info!("initial constraints: {:?}", initial_constraints);
+
+        Ok(Self {
+            env,
+            proc,
+            info,
+            variable_map,
+            translated_fn,
+            return_name,
+            return_synty,
+            inclusion_tracker,
+            collected_procedures: HashMap::new(),
+            procedure_registry,
+            attrs,
+            local_lifetimes: Vec::new(),
+            bb_queue: Vec::new(),
+            processed_bbs: HashSet::new(),
+            ty_translator,
+            loop_specs: HashMap::new(),
+            fn_locals,
+            checked_op_temporaries,
+            initial_constraints,
+            const_registry,
+            trait_registry,
+            collected_statics: HashSet::new(),
+        })
+    }
+
+    /// Main translation function that actually does the translation and returns a `radium::Function`
+    /// if successful.
+    pub fn translate(
+        mut self,
+        spec_arena: &'def Arena<radium::FunctionSpec<'def, radium::InnerFunctionSpec<'def>>>,
+    ) -> Result<radium::Function<'def>, TranslationError<'tcx>> {
+        // add loop info
+        let loop_info = self.proc.loop_info();
+        loop_info.dump_body_head();
+
+        // translate the function's basic blocks
+        let basic_blocks = &self.proc.get_mir().basic_blocks;
+
+        // first translate the initial basic block; we add some additional annotations to the front
+        let initial_bb_idx = BasicBlock::from_u32(0);
+        if let Some(bb) = basic_blocks.get(initial_bb_idx) {
+            let mut translated_bb = self.translate_basic_block(initial_bb_idx, bb)?;
+            // push annotation for initial constraints that relate argument's place regions to universals
+            for (r1, r2) in &self.initial_constraints {
+                translated_bb = radium::Stmt::Annot {
+                    a: radium::Annotation::CopyLftName(
+                        self.ty_translator.format_atomic_region(r1),
+                        self.ty_translator.format_atomic_region(r2),
+                    ),
+                    s: Box::new(translated_bb),
+                    why: Some("initialization".to_owned()),
+                };
+            }
+            self.translated_fn.code.add_basic_block(initial_bb_idx.as_usize(), translated_bb);
+        } else {
+            info!("No basic blocks");
+        }
+
+        while let Some(bb_idx) = self.bb_queue.pop() {
+            let bb = &basic_blocks[bb_idx];
+            let translated_bb = self.translate_basic_block(bb_idx, bb)?;
+            self.translated_fn.code.add_basic_block(bb_idx.as_usize(), translated_bb);
+        }
+
+        // assume that all generics are layoutable
+        {
+            let scope = self.ty_translator.scope.borrow();
+            for ty in scope.generic_scope.tyvars() {
+                self.translated_fn.assume_synty_layoutable(radium::SynType::Literal(ty.syn_type));
+            }
+        }
+        // assume that all used literals are layoutable
+        for g in self.ty_translator.scope.borrow().shim_uses.values() {
+            self.translated_fn.assume_synty_layoutable(g.generate_syn_type_term());
+        }
+        // assume that all used tuples are layoutable
+        for g in self.ty_translator.scope.borrow().tuple_uses.values() {
+            self.translated_fn.assume_synty_layoutable(g.generate_syn_type_term());
+        }
+
+        // TODO: process self.loop_specs
+        // - collect the relevant bb -> def_id mappings for this function (so we can eventually generate the
+        //   definition)
+        // - have a function that takes the def_id and then parses the attributes into a loop invariant
+        for (head, did) in &self.loop_specs {
+            let spec = self.parse_attributes_on_loop_spec_closure(*head, *did);
+            self.translated_fn.register_loop_invariant(head.as_usize(), spec);
+        }
+
+        // generate dependencies on other procedures.
+        for used_proc in self.collected_procedures.values() {
+            self.translated_fn.require_function(used_proc.clone());
+        }
+
+        // generate dependencies on statics
+        for did in &self.collected_statics {
+            let s = self.const_registry.get_static(*did)?;
+            self.translated_fn.require_static(s.to_owned());
+        }
+
+        Ok(self.translated_fn.into_function(spec_arena))
+    }
+}
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Generate a string identifier for a Local.
+    /// Tries to find the Rust source code name of the local, otherwise simply enumerates.
+    /// `used_names` keeps track of the Rust source code names that have already been used.
+    fn make_local_name(mir_body: &Body<'tcx>, local: Local, used_names: &mut HashSet<String>) -> String {
+        if let Some(mir_name) = Self::find_name_for_local(mir_body, local, used_names) {
+            let name = base::strip_coq_ident(&mir_name);
+            used_names.insert(mir_name);
+            name
+        } else {
+            let mut name = "__".to_owned();
+            name.push_str(&local.index().to_string());
+            name
+        }
+    }
+
+    /// Find a source name for a local of a MIR body, if possible.
+    fn find_name_for_local(
+        body: &mir::Body<'tcx>,
+        local: mir::Local,
+        used_names: &HashSet<String>,
+    ) -> Option<String> {
+        let debug_info = &body.var_debug_info;
+
+        for dbg in debug_info {
+            let name = &dbg.name;
+            let val = &dbg.value;
+            if let VarDebugInfoContents::Place(l) = *val {
+                // make sure that the place projection is empty -- otherwise this might just
+                // refer to the capture of a closure
+                if let Some(this_local) = l.as_local() {
+                    if this_local == local {
+                        // around closures, multiple symbols may be mapped to the same name.
+                        // To prevent this from happening, we check that the name hasn't been
+                        // used already
+                        // TODO: find better solution
+                        if !used_names.contains(name.as_str()) {
+                            return Some(name.as_str().to_owned());
+                        }
+                    }
+                }
+            } else {
+                // is this case used when constant propagation happens during MIR construction?
+            }
+        }
+
+        None
+    }
+
+    fn format_region(&self, r: facts::Region) -> String {
+        let lft = self.info.mk_atomic_region(r);
+        self.ty_translator.format_atomic_region(&lft)
+    }
+
+    /// Checks whether a place access descends below a reference.
+    fn check_place_below_reference(&self, place: &Place<'tcx>) -> bool {
+        if self.checked_op_temporaries.contains_key(&place.local) {
+            // temporaries are never below references
+            return false;
+        }
+
+        for (pl, _) in place.iter_projections() {
+            // check if the current ty is a reference that we then descend under with proj
+            let cur_ty_kind = pl.ty(&self.proc.get_mir().local_decls, self.env.tcx()).ty.kind();
+            if let TyKind::Ref(_, _, _) = cur_ty_kind {
+                return true;
+            }
+        }
+
+        false
+    }
+
+    /// Registers a drop shim for a particular type for the translation.
+    #[allow(clippy::unused_self)]
+    const fn register_drop_shim_for(&self, _ty: Ty<'tcx>) {
+        // TODO!
+        //let drop_in_place_did: DefId = search::try_resolve_did(self.env.tcx(), &["std", "ptr",
+        // "drop_in_place"]).unwrap();
+
+        //let x: ty::InstanceDef = ty::InstanceDef::DropGlue(drop_in_place_did, Some(ty));
+        //let body: &'tcx mir::Body = self.env.tcx().mir_shims(x);
+
+        //info!("Generated drop shim for {:?}", ty);
+        //Self::dump_body(body);
+    }
+
+    /// Translate a goto-like jump to `target`.
+    fn translate_goto_like(
+        &mut self,
+        _loc: &Location,
+        target: BasicBlock,
+    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
+        self.enqueue_basic_block(target);
+        let res_stmt = radium::Stmt::GotoBlock(target.as_usize());
+
+        let loop_info = self.proc.loop_info();
+        if loop_info.is_loop_head(target) && !self.loop_specs.contains_key(&target) {
+            let spec_defid = self.find_loop_spec_closure(target)?;
+            self.loop_specs.insert(target, spec_defid);
+        }
+
+        Ok(res_stmt)
+    }
+
+    /// Enqueues a basic block for processing, if it has not already been processed,
+    /// and marks it as having been processed.
+    fn enqueue_basic_block(&mut self, bb: BasicBlock) {
+        if !self.processed_bbs.contains(&bb) {
+            self.bb_queue.push(bb);
+            self.processed_bbs.insert(bb);
+        }
+    }
+
+    /// Prepend endlft annotations for dying loans to a statement.
+    fn prepend_endlfts<I>(&self, st: radium::Stmt, dying: I) -> radium::Stmt
+    where
+        I: ExactSizeIterator<Item = facts::Loan>,
+    {
+        let mut cont_stmt = st;
+        if dying.len() > 0 {
+            //info!("Dying at {:?}: {:?}", loc, dying);
+            for d in dying {
+                let lft = self.info.atomic_region_of_loan(d);
+                cont_stmt = radium::Stmt::Annot {
+                    a: radium::Annotation::EndLft(self.ty_translator.format_atomic_region(&lft)),
+                    s: Box::new(cont_stmt),
+                    why: Some("endlft".to_owned()),
+                };
+            }
+        }
+        cont_stmt
+    }
+
+    /// Make a trivial place accessing `local`.
+    fn make_local_place(&self, local: Local) -> Place<'tcx> {
+        Place {
+            local,
+            projection: self.env.tcx().mk_place_elems(&[]),
+        }
+    }
+
+    /// Get the type of a local in a body.
+    fn get_type_of_local(&self, local: Local) -> Result<Ty<'tcx>, TranslationError<'tcx>> {
+        self.proc
+            .get_mir()
+            .local_decls
+            .get(local)
+            .map(|decl| decl.ty)
+            .ok_or_else(|| TranslationError::UnknownVar(String::new()))
+    }
+
+    /// Get the type of a place expression.
+    fn get_type_of_place(&self, pl: &Place<'tcx>) -> PlaceTy<'tcx> {
+        pl.ty(&self.proc.get_mir().local_decls, self.env.tcx())
+    }
+
+    /// Get the type of a const.
+    fn get_type_of_const(cst: &Constant<'tcx>) -> Ty<'tcx> {
+        match cst.literal {
+            ConstantKind::Ty(cst) => cst.ty(),
+            ConstantKind::Val(_, ty) | ConstantKind::Unevaluated(_, ty) => ty,
+        }
+    }
+
+    /// Get the type of an operand.
+    fn get_type_of_operand(&self, op: &Operand<'tcx>) -> Ty<'tcx> {
+        op.ty(&self.proc.get_mir().local_decls, self.env.tcx())
+    }
+
+    /// Check if a local is used for a spec closure.
+    fn is_spec_closure_local(&self, l: Local) -> Result<Option<DefId>, TranslationError<'tcx>> {
+        // check if we should ignore this
+        let local_type = self.get_type_of_local(l)?;
+
+        let TyKind::Closure(did, _) = local_type.kind() else {
+            return Ok(None);
+        };
+
+        Ok(self
+            .procedure_registry
+            .lookup_function_mode(*did)
+            .and_then(|m| m.is_ignore().then_some(*did)))
+    }
+}
diff --git a/rr_frontend/translation/src/body/translation/place.rs b/rr_frontend/translation/src/body/translation/place.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e25574446872ee6f7d0b44466ff6222d284af1ff
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/place.rs
@@ -0,0 +1,139 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use super::TX;
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Translate a place to a Caesium lvalue.
+    pub(super) fn translate_place(
+        &mut self,
+        pl: &Place<'tcx>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        // Get the type of the underlying local. We will use this to
+        // get the necessary layout information for dereferencing
+        let mut cur_ty = self.get_type_of_local(pl.local).map(PlaceTy::from_ty)?;
+
+        let local_name = self
+            .variable_map
+            .get(&pl.local)
+            .ok_or_else(|| TranslationError::UnknownVar(format!("{:?}", pl.local)))?;
+
+        let mut acc_expr = radium::Expr::Var(local_name.to_string());
+
+        // iterate in evaluation order
+        for it in pl.projection {
+            match &it {
+                ProjectionElem::Deref => {
+                    // use the type of the dereferencee
+                    let st = self.ty_translator.translate_type_to_syn_type(cur_ty.ty)?;
+                    acc_expr = radium::Expr::Deref {
+                        ot: st.into(),
+                        e: Box::new(acc_expr),
+                    };
+                },
+                ProjectionElem::Field(f, _) => {
+                    // `t` is the type of the field we are accessing!
+                    let lit = self.ty_translator.generate_structlike_use(cur_ty.ty, cur_ty.variant_index)?;
+                    // TODO: does not do the right thing for accesses to fields of zero-sized objects.
+                    let struct_sls = lit.map_or(radium::SynType::Unit, |x| x.generate_raw_syn_type_term());
+                    let name = self.ty_translator.translator.get_field_name_of(
+                        *f,
+                        cur_ty.ty,
+                        cur_ty.variant_index.map(abi::VariantIdx::as_usize),
+                    )?;
+
+                    acc_expr = radium::Expr::FieldOf {
+                        e: Box::new(acc_expr),
+                        name,
+                        sls: struct_sls.to_string(),
+                    };
+                },
+                ProjectionElem::Index(_v) => {
+                    //TODO
+                    return Err(TranslationError::UnsupportedFeature {
+                        description: "places: implement index access".to_owned(),
+                    });
+                },
+                ProjectionElem::ConstantIndex { .. } => {
+                    //TODO
+                    return Err(TranslationError::UnsupportedFeature {
+                        description: "places: implement const index access".to_owned(),
+                    });
+                },
+                ProjectionElem::Subslice { .. } => {
+                    return Err(TranslationError::UnsupportedFeature {
+                        description: "places: implement subslicing".to_owned(),
+                    });
+                },
+                ProjectionElem::Downcast(_, variant_idx) => {
+                    info!("Downcast of ty {:?} to {:?}", cur_ty, variant_idx);
+                    if let ty::TyKind::Adt(def, args) = cur_ty.ty.kind() {
+                        if def.is_enum() {
+                            let enum_use = self.ty_translator.generate_enum_use(*def, args.iter())?;
+                            let els = enum_use.generate_raw_syn_type_term();
+
+                            let variant_name = types::TX::get_variant_name_of(cur_ty.ty, *variant_idx)?;
+
+                            acc_expr = radium::Expr::EnumData {
+                                els: els.to_string(),
+                                variant: variant_name,
+                                e: Box::new(acc_expr),
+                            }
+                        } else {
+                            return Err(TranslationError::UnknownError(
+                                "places: ADT downcasting on non-enum type".to_owned(),
+                            ));
+                        }
+                    } else {
+                        return Err(TranslationError::UnknownError(
+                            "places: ADT downcasting on non-enum type".to_owned(),
+                        ));
+                    }
+                },
+                ProjectionElem::OpaqueCast(_) => {
+                    return Err(TranslationError::UnsupportedFeature {
+                        description: "places: implement opaque casts".to_owned(),
+                    });
+                },
+            };
+            // update cur_ty
+            cur_ty = cur_ty.projection_ty(self.env.tcx(), it);
+        }
+        info!("translating place {:?} to {:?}", pl, acc_expr);
+        Ok(acc_expr)
+    }
+}
diff --git a/rr_frontend/translation/src/body/translation/rvalue.rs b/rr_frontend/translation/src/body/translation/rvalue.rs
new file mode 100644
index 0000000000000000000000000000000000000000..426d3c021455f5ac5c86f1c404016c9c4b6b2cdc
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/rvalue.rs
@@ -0,0 +1,638 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::index::IndexVec;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use super::TX;
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Translate an aggregate expression.
+    fn translate_aggregate(
+        &mut self,
+        loc: Location,
+        kind: &mir::AggregateKind<'tcx>,
+        op: &IndexVec<abi::FieldIdx, mir::Operand<'tcx>>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        // translate operands
+        let mut translated_ops: Vec<radium::Expr> = Vec::new();
+        let mut operand_types: Vec<Ty<'tcx>> = Vec::new();
+
+        for o in op {
+            let translated_o = self.translate_operand(o, true)?;
+            let type_of_o = self.get_type_of_operand(o);
+            translated_ops.push(translated_o);
+            operand_types.push(type_of_o);
+        }
+
+        match *kind {
+            mir::AggregateKind::Tuple => {
+                if operand_types.is_empty() {
+                    // translate to unit literal
+                    return Ok(radium::Expr::Literal(radium::Literal::ZST));
+                }
+
+                let struct_use = self.ty_translator.generate_tuple_use(operand_types.iter().copied())?;
+                let sl = struct_use.generate_raw_syn_type_term();
+                let initializers: Vec<_> =
+                    translated_ops.into_iter().enumerate().map(|(i, o)| (i.to_string(), o)).collect();
+
+                Ok(radium::Expr::StructInitE {
+                    sls: coq::term::App::new_lhs(sl.to_string()),
+                    components: initializers,
+                })
+            },
+
+            mir::AggregateKind::Adt(did, variant, args, ..) => {
+                // get the adt def
+                let adt_def: ty::AdtDef<'tcx> = self.env.tcx().adt_def(did);
+
+                if adt_def.is_struct() {
+                    let variant = adt_def.variant(variant);
+                    let struct_use = self.ty_translator.generate_struct_use(variant.def_id, args)?;
+
+                    let Some(struct_use) = struct_use else {
+                        // if not, it's replaced by unit
+                        return Ok(radium::Expr::Literal(radium::Literal::ZST));
+                    };
+
+                    let sl = struct_use.generate_raw_syn_type_term();
+                    let initializers: Vec<_> = translated_ops
+                        .into_iter()
+                        .zip(variant.fields.iter())
+                        .map(|(o, field)| (field.name.to_string(), o))
+                        .collect();
+
+                    return Ok(radium::Expr::StructInitE {
+                        sls: coq::term::App::new_lhs(sl.to_string()),
+                        components: initializers,
+                    });
+                }
+
+                if adt_def.is_enum() {
+                    let variant_def = adt_def.variant(variant);
+
+                    let struct_use =
+                        self.ty_translator.generate_enum_variant_use(variant_def.def_id, args)?;
+                    let sl = struct_use.generate_raw_syn_type_term();
+
+                    let initializers: Vec<_> = translated_ops
+                        .into_iter()
+                        .zip(variant_def.fields.iter())
+                        .map(|(o, field)| (field.name.to_string(), o))
+                        .collect();
+
+                    let variant_e = radium::Expr::StructInitE {
+                        sls: coq::term::App::new_lhs(sl.to_string()),
+                        components: initializers,
+                    };
+
+                    let enum_use = self.ty_translator.generate_enum_use(adt_def, args)?;
+                    let els = enum_use.generate_raw_syn_type_term();
+
+                    info!("generating enum annotation for type {:?}", enum_use);
+                    let ty = radium::RustType::of_type(&radium::Type::Literal(enum_use));
+                    let variant_name = variant_def.name.to_string();
+
+                    return Ok(radium::Expr::EnumInitE {
+                        els: coq::term::App::new_lhs(els.to_string()),
+                        variant: variant_name,
+                        ty,
+                        initializer: Box::new(variant_e),
+                    });
+                }
+
+                // TODO
+                Err(TranslationError::UnsupportedFeature {
+                    description: format!(
+                        "RefinedRust does currently not support aggregate rvalue for other ADTs (got: {kind:?}, {op:?})"
+                    ),
+                })
+            },
+            mir::AggregateKind::Closure(def, _args) => {
+                trace!("Translating Closure aggregate value for {:?}", def);
+
+                // We basically translate this to a tuple
+                if operand_types.is_empty() {
+                    // translate to unit literal
+                    return Ok(radium::Expr::Literal(radium::Literal::ZST));
+                }
+
+                let struct_use = self.ty_translator.generate_tuple_use(operand_types.iter().copied())?;
+                let sl = struct_use.generate_raw_syn_type_term();
+
+                let initializers: Vec<_> =
+                    translated_ops.into_iter().enumerate().map(|(i, o)| (i.to_string(), o)).collect();
+
+                Ok(radium::Expr::StructInitE {
+                    sls: coq::term::App::new_lhs(sl.to_string()),
+                    components: initializers,
+                })
+            },
+
+            _ => {
+                // TODO
+                Err(TranslationError::UnsupportedFeature {
+                    description: format!(
+                        "RefinedRust does currently not support this kind of aggregate rvalue (got: {kind:?}, {op:?})"
+                    ),
+                })
+            },
+        }
+    }
+
+    fn translate_cast(
+        &mut self,
+        kind: mir::CastKind,
+        op: &mir::Operand<'tcx>,
+        to_ty: ty::Ty<'tcx>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        let op_ty = self.get_type_of_operand(op);
+        let op_st = self.ty_translator.translate_type_to_syn_type(op_ty)?;
+        let op_ot = op_st.into();
+
+        let translated_op = self.translate_operand(op, true)?;
+
+        let target_st = self.ty_translator.translate_type_to_syn_type(to_ty)?;
+        let target_ot = target_st.into();
+
+        match kind {
+            mir::CastKind::PointerCoercion(x) => {
+                match x {
+                    ty::adjustment::PointerCoercion::MutToConstPointer => {
+                        // this is a NOP in our model
+                        Ok(translated_op)
+                    },
+
+                    ty::adjustment::PointerCoercion::ArrayToPointer
+                    | ty::adjustment::PointerCoercion::ClosureFnPointer(_)
+                    | ty::adjustment::PointerCoercion::ReifyFnPointer
+                    | ty::adjustment::PointerCoercion::UnsafeFnPointer
+                    | ty::adjustment::PointerCoercion::Unsize => Err(TranslationError::UnsupportedFeature {
+                        description: format!(
+                            "RefinedRust does currently not support this kind of pointer coercion (got: {kind:?})"
+                        ),
+                    }),
+                }
+            },
+
+            mir::CastKind::DynStar => Err(TranslationError::UnsupportedFeature {
+                description: "RefinedRust does currently not support dyn* cast".to_owned(),
+            }),
+
+            mir::CastKind::IntToInt => {
+                // Cast integer to integer
+                Ok(radium::Expr::UnOp {
+                    o: radium::Unop::Cast(target_ot),
+                    ot: op_ot,
+                    e: Box::new(translated_op),
+                })
+            },
+
+            mir::CastKind::IntToFloat => Err(TranslationError::UnsupportedFeature {
+                description: "RefinedRust does currently not support int-to-float cast".to_owned(),
+            }),
+
+            mir::CastKind::FloatToInt => Err(TranslationError::UnsupportedFeature {
+                description: "RefinedRust does currently not support float-to-int cast".to_owned(),
+            }),
+
+            mir::CastKind::FloatToFloat => Err(TranslationError::UnsupportedFeature {
+                description: "RefinedRust does currently not support float-to-float cast".to_owned(),
+            }),
+
+            mir::CastKind::PtrToPtr => {
+                match (op_ty.kind(), to_ty.kind()) {
+                    (TyKind::RawPtr(_), TyKind::RawPtr(_)) => {
+                        // Casts between raw pointers are NOPs for us
+                        Ok(translated_op)
+                    },
+
+                    _ => {
+                        // TODO: any other cases we should handle?
+                        Err(TranslationError::UnsupportedFeature {
+                            description: format!(
+                                "RefinedRust does currently not support ptr-to-ptr cast (got: {kind:?}, {op:?}, {to_ty:?})"
+                            ),
+                        })
+                    },
+                }
+            },
+
+            mir::CastKind::FnPtrToPtr => Err(TranslationError::UnsupportedFeature {
+                description: format!(
+                    "RefinedRust does currently not support fnptr-to-ptr cast (got: {kind:?}, {op:?}, {to_ty:?})"
+                ),
+            }),
+
+            mir::CastKind::Transmute => Err(TranslationError::UnsupportedFeature {
+                description: format!(
+                    "RefinedRust does currently not support transmute cast (got: {kind:?}, {op:?}, {to_ty:?})"
+                ),
+            }),
+
+            mir::CastKind::PointerExposeAddress => {
+                // Cast pointer to integer
+                Ok(radium::Expr::UnOp {
+                    o: radium::Unop::Cast(target_ot),
+                    ot: radium::OpType::Ptr,
+                    e: Box::new(translated_op),
+                })
+            },
+
+            mir::CastKind::PointerFromExposedAddress => Err(TranslationError::UnsupportedFeature {
+                description: format!(
+                    "RefinedRust does currently not support this kind of cast (got: {kind:?}, {op:?}, {to_ty:?})"
+                ),
+            }),
+        }
+    }
+
+    /// Translate an operand.
+    /// This will either generate an lvalue (in case of Move or Copy) or an rvalue (in most cases
+    /// of Constant). How this is used depends on the context. (e.g., Use of an integer constant
+    /// does not typecheck, and produces a stuck program).
+    pub(super) fn translate_operand(
+        &mut self,
+        op: &Operand<'tcx>,
+        to_rvalue: bool,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        match op {
+            // In Caesium: typed_place needs deref (not use) for place accesses.
+            // use is used top-level to convert an lvalue to an rvalue, which is why we use it here.
+            Operand::Copy(place) | Operand::Move(place) => {
+                // check if this goes to a temporary of a checked op
+                let place_kind = if self.checked_op_temporaries.contains_key(&place.local) {
+                    assert!(place.projection.len() == 1);
+
+                    let ProjectionElem::Field(f, _0) = place.projection[0] else {
+                        unreachable!("invariant violation for access to checked op temporary");
+                    };
+
+                    if f.index() != 0 {
+                        // make this a constant false -- our semantics directly checks for overflows
+                        // and otherwise throws UB.
+                        return Ok(radium::Expr::Literal(radium::Literal::Bool(false)));
+                    }
+
+                    // access to the result of the op
+                    self.make_local_place(place.local)
+                } else {
+                    *place
+                };
+
+                let translated_place = self.translate_place(&place_kind)?;
+                let ty = self.get_type_of_place(place);
+
+                let st = self.ty_translator.translate_type_to_syn_type(ty.ty)?;
+
+                if to_rvalue {
+                    Ok(radium::Expr::Use {
+                        ot: st.into(),
+                        e: Box::new(translated_place),
+                    })
+                } else {
+                    Ok(translated_place)
+                }
+            },
+            Operand::Constant(constant) => {
+                // TODO: possibly need different handling of the rvalue flag
+                // when this also handles string literals etc.
+                return self.translate_constant(constant.as_ref());
+            },
+        }
+    }
+
+    /// Translates an Rvalue.
+    pub(super) fn translate_rvalue(
+        &mut self,
+        loc: Location,
+        rval: &Rvalue<'tcx>,
+    ) -> Result<radium::Expr, TranslationError<'tcx>> {
+        match rval {
+            Rvalue::Use(op) => {
+                // converts an lvalue to an rvalue
+                self.translate_operand(op, true)
+            },
+
+            Rvalue::Ref(region, bk, pl) => {
+                let translated_pl = self.translate_place(pl)?;
+                let translated_bk = TX::translate_borrow_kind(*bk)?;
+                let ty_annot = self.get_type_annotation_for_borrow(*bk, pl)?;
+
+                if let Some(loan) = self.info.get_optional_loan_at_location(loc) {
+                    let atomic_region = self.info.atomic_region_of_loan(loan);
+                    let lft = self.ty_translator.format_atomic_region(&atomic_region);
+                    Ok(radium::Expr::Borrow {
+                        lft,
+                        bk: translated_bk,
+                        ty: ty_annot,
+                        e: Box::new(translated_pl),
+                    })
+                } else {
+                    info!("Didn't find loan at {:?}: {:?}; region {:?}", loc, rval, region);
+                    let region = regions::region_to_region_vid(*region);
+                    let lft = self.format_region(region);
+
+                    Ok(radium::Expr::Borrow {
+                        lft,
+                        bk: translated_bk,
+                        ty: ty_annot,
+                        e: Box::new(translated_pl),
+                    })
+                }
+            },
+
+            Rvalue::AddressOf(mt, pl) => {
+                let translated_pl = self.translate_place(pl)?;
+                let translated_mt = TX::translate_mutability(*mt);
+
+                Ok(radium::Expr::AddressOf {
+                    mt: translated_mt,
+                    e: Box::new(translated_pl),
+                })
+            },
+
+            Rvalue::BinaryOp(op, operands) => {
+                let e1 = &operands.as_ref().0;
+                let e2 = &operands.as_ref().1;
+
+                let e1_ty = self.get_type_of_operand(e1);
+                let e2_ty = self.get_type_of_operand(e2);
+                let e1_st = self.ty_translator.translate_type_to_syn_type(e1_ty)?;
+                let e2_st = self.ty_translator.translate_type_to_syn_type(e2_ty)?;
+
+                let translated_e1 = self.translate_operand(e1, true)?;
+                let translated_e2 = self.translate_operand(e2, true)?;
+                let translated_op = self.translate_binop(*op, &operands.as_ref().0, &operands.as_ref().1)?;
+
+                Ok(radium::Expr::BinOp {
+                    o: translated_op,
+                    ot1: e1_st.into(),
+                    ot2: e2_st.into(),
+                    e1: Box::new(translated_e1),
+                    e2: Box::new(translated_e2),
+                })
+            },
+
+            Rvalue::CheckedBinaryOp(op, operands) => {
+                let e1 = &operands.as_ref().0;
+                let e2 = &operands.as_ref().1;
+
+                let e1_ty = self.get_type_of_operand(e1);
+                let e2_ty = self.get_type_of_operand(e2);
+                let e1_st = self.ty_translator.translate_type_to_syn_type(e1_ty)?;
+                let e2_st = self.ty_translator.translate_type_to_syn_type(e2_ty)?;
+
+                let translated_e1 = self.translate_operand(e1, true)?;
+                let translated_e2 = self.translate_operand(e2, true)?;
+                let translated_op = TX::translate_checked_binop(*op)?;
+
+                Ok(radium::Expr::BinOp {
+                    o: translated_op,
+                    ot1: e1_st.into(),
+                    ot2: e2_st.into(),
+                    e1: Box::new(translated_e1),
+                    e2: Box::new(translated_e2),
+                })
+            },
+
+            Rvalue::UnaryOp(op, operand) => {
+                let translated_e1 = self.translate_operand(operand, true)?;
+                let e1_ty = self.get_type_of_operand(operand);
+                let e1_st = self.ty_translator.translate_type_to_syn_type(e1_ty)?;
+                let translated_op = TX::translate_unop(*op, e1_ty)?;
+
+                Ok(radium::Expr::UnOp {
+                    o: translated_op,
+                    ot: e1_st.into(),
+                    e: Box::new(translated_e1),
+                })
+            },
+
+            Rvalue::NullaryOp(op, _ty) => {
+                // TODO: SizeOf
+                Err(TranslationError::UnsupportedFeature {
+                    description: "RefinedRust does currently not support nullary ops (AlignOf, Sizeof)"
+                        .to_owned(),
+                })
+            },
+
+            Rvalue::Discriminant(pl) => {
+                let ty = self.get_type_of_place(pl);
+                let translated_pl = self.translate_place(pl)?;
+                info!("getting discriminant of {:?} at type {:?}", pl, ty);
+
+                let ty::TyKind::Adt(adt_def, args) = ty.ty.kind() else {
+                    return Err(TranslationError::UnsupportedFeature {
+                        description: format!(
+                            "RefinedRust does currently not support discriminant accesses on non-enum types ({:?}, got {:?})",
+                            rval, ty.ty
+                        ),
+                    });
+                };
+
+                let enum_use = self.ty_translator.generate_enum_use(*adt_def, args.iter())?;
+                let els = enum_use.generate_raw_syn_type_term();
+
+                let discriminant_acc = radium::Expr::EnumDiscriminant {
+                    els: els.to_string(),
+                    e: Box::new(translated_pl),
+                };
+
+                // need to do a load from this place
+                let it = ty.ty.discriminant_ty(self.env.tcx());
+                let translated_it = self.ty_translator.translate_type(it)?;
+
+                let radium::Type::Int(translated_it) = translated_it else {
+                    return Err(TranslationError::UnknownError(format!(
+                        "type of discriminant is not an integer type {:?}",
+                        it
+                    )));
+                };
+
+                let ot = radium::OpType::Int(translated_it);
+
+                Ok(radium::Expr::Use {
+                    ot,
+                    e: Box::new(discriminant_acc),
+                })
+            },
+
+            Rvalue::Aggregate(kind, op) => self.translate_aggregate(loc, kind.as_ref(), op),
+
+            Rvalue::Cast(kind, op, to_ty) => self.translate_cast(*kind, op, *to_ty),
+
+            Rvalue::CopyForDeref(_)
+            | Rvalue::Len(..)
+            | Rvalue::Repeat(..)
+            | Rvalue::ThreadLocalRef(..)
+            | Rvalue::ShallowInitBox(_, _) => Err(TranslationError::UnsupportedFeature {
+                description: format!(
+                    "RefinedRust does currently not support this kind of rvalue (got: {:?})",
+                    rval
+                ),
+            }),
+        }
+    }
+
+    /// Get the type to annotate a borrow with.
+    fn get_type_annotation_for_borrow(
+        &self,
+        bk: BorrowKind,
+        pl: &Place<'tcx>,
+    ) -> Result<Option<radium::RustType>, TranslationError<'tcx>> {
+        let BorrowKind::Mut { .. } = bk else {
+            return Ok(None);
+        };
+
+        let ty = self.get_type_of_place(pl);
+
+        // For borrows, we can safely ignore the downcast type -- we cannot borrow a particularly variant
+        let translated_ty = self.ty_translator.translate_type(ty.ty)?;
+        let annot_ty = radium::RustType::of_type(&translated_ty);
+
+        Ok(Some(annot_ty))
+    }
+
+    /// Translate binary operators.
+    /// We need access to the operands, too, to handle the offset operator and get the right
+    /// Caesium layout annotation.
+    fn translate_binop(
+        &self,
+        op: BinOp,
+        e1: &Operand<'tcx>,
+        _e2: &Operand<'tcx>,
+    ) -> Result<radium::Binop, TranslationError<'tcx>> {
+        match op {
+            BinOp::Add | BinOp::AddUnchecked => Ok(radium::Binop::Add),
+            BinOp::Sub | BinOp::SubUnchecked => Ok(radium::Binop::Sub),
+            BinOp::Mul | BinOp::MulUnchecked => Ok(radium::Binop::Mul),
+            BinOp::Div => Ok(radium::Binop::Div),
+            BinOp::Rem => Ok(radium::Binop::Mod),
+
+            BinOp::BitXor => Ok(radium::Binop::BitXor),
+            BinOp::BitAnd => Ok(radium::Binop::BitAnd),
+            BinOp::BitOr => Ok(radium::Binop::BitOr),
+            BinOp::Shl | BinOp::ShlUnchecked => Ok(radium::Binop::Shl),
+            BinOp::Shr | BinOp::ShrUnchecked => Ok(radium::Binop::Shr),
+
+            BinOp::Eq => Ok(radium::Binop::Eq),
+            BinOp::Lt => Ok(radium::Binop::Lt),
+            BinOp::Le => Ok(radium::Binop::Le),
+            BinOp::Ne => Ok(radium::Binop::Ne),
+            BinOp::Ge => Ok(radium::Binop::Ge),
+            BinOp::Gt => Ok(radium::Binop::Gt),
+
+            BinOp::Offset => {
+                // we need to get the layout of the thing we're offsetting
+                // try to get the type of e1.
+                let e1_ty = self.get_type_of_operand(e1);
+                let off_ty = TX::get_offset_ty(e1_ty)?;
+                let st = self.ty_translator.translate_type_to_syn_type(off_ty)?;
+                let ly = st.into();
+                Ok(radium::Binop::PtrOffset(ly))
+            },
+        }
+    }
+
+    /// Get the inner type of a type to which we can apply the offset operator.
+    fn get_offset_ty(ty: Ty<'tcx>) -> Result<Ty<'tcx>, TranslationError<'tcx>> {
+        match ty.kind() {
+            TyKind::Array(t, _) | TyKind::Slice(t) | TyKind::Ref(_, t, _) => Ok(*t),
+            TyKind::RawPtr(tm) => Ok(tm.ty),
+            _ => Err(TranslationError::UnknownError(format!("cannot take offset of {}", ty))),
+        }
+    }
+
+    /// Translate checked binary operators.
+    /// We need access to the operands, too, to handle the offset operator and get the right
+    /// Caesium layout annotation.
+    fn translate_checked_binop(op: BinOp) -> Result<radium::Binop, TranslationError<'tcx>> {
+        match op {
+            BinOp::Add => Ok(radium::Binop::CheckedAdd),
+            BinOp::Sub => Ok(radium::Binop::CheckedSub),
+            BinOp::Mul => Ok(radium::Binop::CheckedMul),
+            BinOp::Shl => Err(TranslationError::UnsupportedFeature {
+                description: "RefinedRust does currently not support checked Shl".to_owned(),
+            }),
+            BinOp::Shr => Err(TranslationError::UnsupportedFeature {
+                description: "RefinedRust does currently not support checked Shr".to_owned(),
+            }),
+            _ => Err(TranslationError::UnknownError(
+                "unexpected checked binop that is not Add, Sub, Mul, Shl, or Shr".to_owned(),
+            )),
+        }
+    }
+
+    /// Translate unary operators.
+    fn translate_unop(op: UnOp, ty: Ty<'tcx>) -> Result<radium::Unop, TranslationError<'tcx>> {
+        match op {
+            UnOp::Not => match ty.kind() {
+                ty::TyKind::Bool => Ok(radium::Unop::NotBool),
+                ty::TyKind::Int(_) | ty::TyKind::Uint(_) => Ok(radium::Unop::NotInt),
+                _ => Err(TranslationError::UnknownError(
+                    "application of UnOp::Not to non-{Int, Bool}".to_owned(),
+                )),
+            },
+            UnOp::Neg => Ok(radium::Unop::Neg),
+        }
+    }
+
+    /// Translate a `BorrowKind`.
+    fn translate_borrow_kind(kind: BorrowKind) -> Result<radium::BorKind, TranslationError<'tcx>> {
+        match kind {
+            BorrowKind::Shared => Ok(radium::BorKind::Shared),
+            BorrowKind::Shallow => {
+                // TODO: figure out what to do with this
+                // arises in match lowering
+                Err(TranslationError::UnsupportedFeature {
+                    description: "RefinedRust does currently not support shallow borrows".to_owned(),
+                })
+            },
+            BorrowKind::Mut { .. } => {
+                // TODO: handle two-phase borrows?
+                Ok(radium::BorKind::Mutable)
+            },
+        }
+    }
+
+    const fn translate_mutability(mt: Mutability) -> radium::Mutability {
+        match mt {
+            Mutability::Mut => radium::Mutability::Mut,
+            Mutability::Not => radium::Mutability::Shared,
+        }
+    }
+}
diff --git a/rr_frontend/translation/src/body/translation/terminator.rs b/rr_frontend/translation/src/body/translation/terminator.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4f73ccc98d3ee82256d03940440b826317569d0b
--- /dev/null
+++ b/rr_frontend/translation/src/body/translation/terminator.rs
@@ -0,0 +1,254 @@
+// © 2023, The RefinedRust Developers and Contributors
+//
+// This Source Code Form is subject to the terms of the BSD-3-clause License.
+// If a copy of the BSD-3-clause license was not distributed with this
+// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
+
+use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
+
+use log::{info, trace, warn};
+use radium::coq;
+use rr_rustc_interface::hir::def_id::DefId;
+use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
+use rr_rustc_interface::middle::mir::tcx::PlaceTy;
+use rr_rustc_interface::middle::mir::{
+    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
+    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
+    TerminatorKind, UnOp, VarDebugInfoContents,
+};
+use rr_rustc_interface::middle::ty::fold::TypeFolder;
+use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
+use rr_rustc_interface::middle::{mir, ty};
+use rr_rustc_interface::{abi, ast, middle};
+use typed_arena::Arena;
+
+use super::TX;
+use crate::base::*;
+use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
+use crate::environment::borrowck::facts;
+use crate::environment::polonius_info::PoloniusInfo;
+use crate::environment::procedure::Procedure;
+use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::spec_parsers::parse_utils::ParamLookup;
+use crate::spec_parsers::verbose_function_spec_parser::{
+    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
+};
+use crate::traits::{registry, resolution};
+use crate::{base, consts, procedures, regions, search, traits, types};
+
+impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
+    /// Check if a call goes to `std::rt::begin_panic`
+    fn is_call_destination_panic(&mut self, func: &Operand) -> bool {
+        let Operand::Constant(box c) = func else {
+            return false;
+        };
+
+        let ConstantKind::Val(_, ty) = c.literal else {
+            return false;
+        };
+
+        let TyKind::FnDef(did, _) = ty.kind() else {
+            return false;
+        };
+
+        if let Some(panic_id_std) =
+            search::try_resolve_did(self.env.tcx(), &["std", "panicking", "begin_panic"])
+        {
+            if panic_id_std == *did {
+                return true;
+            }
+        } else {
+            warn!("Failed to determine DefId of std::panicking::begin_panic");
+        }
+
+        if let Some(panic_id_core) = search::try_resolve_did(self.env.tcx(), &["core", "panicking", "panic"])
+        {
+            if panic_id_core == *did {
+                return true;
+            }
+        } else {
+            warn!("Failed to determine DefId of core::panicking::panic");
+        }
+
+        false
+    }
+
+    /// Translate a terminator.
+    /// We pass the dying loans during this terminator. They need to be added at the right
+    /// intermediate point.
+    pub(super) fn translate_terminator(
+        &mut self,
+        term: &Terminator<'tcx>,
+        loc: Location,
+        dying_loans: Vec<facts::Loan>,
+    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
+        match &term.kind {
+            TerminatorKind::Goto { target } => self.translate_goto_like(&loc, *target),
+
+            TerminatorKind::Call {
+                func,
+                args,
+                destination,
+                target,
+                ..
+            } => {
+                trace!("translating Call {:?}", term);
+                if self.is_call_destination_panic(func) {
+                    info!("Replacing call to std::panicking::begin_panic with Stuck");
+                    return Ok(radium::Stmt::Stuck);
+                }
+
+                self.translate_function_call(func, args, destination, *target, loc, dying_loans.as_slice())
+            },
+
+            TerminatorKind::Return => {
+                // TODO: this requires additional handling for reborrows
+
+                // read from the return place
+                // Is this semantics accurate wrt what the intended MIR semantics is?
+                // Possibly handle this differently by making the first argument of a function a dedicated
+                // return place? See also discussion at https://github.com/rust-lang/rust/issues/71117
+                let stmt = radium::Stmt::Return(radium::Expr::Use {
+                    ot: (&self.return_synty).into(),
+                    e: Box::new(radium::Expr::Var(self.return_name.clone())),
+                });
+
+                // TODO is this right?
+                Ok(self.prepend_endlfts(stmt, dying_loans.into_iter()))
+            },
+
+            //TerminatorKind::Abort => {
+            //res_stmt = radium::Stmt::Stuck;
+            //res_stmt = self.prepend_endlfts(res_stmt, dying_loans.into_iter());
+            //},
+            TerminatorKind::SwitchInt { discr, targets } => {
+                let operand = self.translate_operand(discr, true)?;
+                let all_targets: &[BasicBlock] = targets.all_targets();
+
+                if self.get_type_of_operand(discr).is_bool() {
+                    // we currently special-case this as Caesium has a built-in if and this is more
+                    // convenient to handle for the type-checker
+
+                    // implementation detail: the first index is the `false` branch, the second the
+                    // `true` branch
+                    let true_target = all_targets[1];
+                    let false_target = all_targets[0];
+
+                    let true_branch = self.translate_goto_like(&loc, true_target)?;
+                    let false_branch = self.translate_goto_like(&loc, false_target)?;
+
+                    let stmt = radium::Stmt::If {
+                        e: operand,
+                        ot: radium::OpType::Bool,
+                        s1: Box::new(true_branch),
+                        s2: Box::new(false_branch),
+                    };
+
+                    // TODO: is this right?
+                    return Ok(self.prepend_endlfts(stmt, dying_loans.into_iter()));
+                }
+
+                //info!("switchint: {:?}", term.kind);
+                let operand = self.translate_operand(discr, true)?;
+                let ty = self.get_type_of_operand(discr);
+
+                let mut target_map: HashMap<u128, usize> = HashMap::new();
+                let mut translated_targets: Vec<radium::Stmt> = Vec::new();
+
+                for (idx, (tgt, bb)) in targets.iter().enumerate() {
+                    let bb: BasicBlock = bb;
+                    let translated_target = self.translate_goto_like(&loc, bb)?;
+
+                    target_map.insert(tgt, idx);
+                    translated_targets.push(translated_target);
+                }
+
+                let translated_default = self.translate_goto_like(&loc, targets.otherwise())?;
+                // TODO: need to put endlfts infront of gotos?
+
+                let translated_ty = self.ty_translator.translate_type(ty)?;
+                let radium::Type::Int(it) = translated_ty else {
+                    return Err(TranslationError::UnknownError(
+                        "SwitchInt switching on non-integer type".to_owned(),
+                    ));
+                };
+
+                Ok(radium::Stmt::Switch {
+                    e: operand,
+                    it,
+                    index_map: target_map,
+                    bs: translated_targets,
+                    def: Box::new(translated_default),
+                })
+            },
+
+            TerminatorKind::Assert {
+                cond,
+                expected,
+                target,
+                ..
+            } => {
+                // this translation gets stuck on failure
+                let cond_translated = self.translate_operand(cond, true)?;
+                let comp = radium::Expr::BinOp {
+                    o: radium::Binop::Eq,
+                    ot1: radium::OpType::Bool,
+                    ot2: radium::OpType::Bool,
+                    e1: Box::new(cond_translated),
+                    e2: Box::new(radium::Expr::Literal(radium::Literal::Bool(*expected))),
+                };
+
+                let stmt = self.translate_goto_like(&loc, *target)?;
+
+                // TODO: should we really have this?
+                let stmt = self.prepend_endlfts(stmt, dying_loans.into_iter());
+
+                Ok(radium::Stmt::AssertS {
+                    e: comp,
+                    s: Box::new(stmt),
+                })
+            },
+
+            TerminatorKind::Drop { place, target, .. } => {
+                let ty = self.get_type_of_place(place);
+                self.register_drop_shim_for(ty.ty);
+
+                let place_translated = self.translate_place(place)?;
+                let _drope = radium::Expr::DropE(Box::new(place_translated));
+
+                let stmt = self.translate_goto_like(&loc, *target)?;
+
+                Ok(self.prepend_endlfts(stmt, dying_loans.into_iter()))
+
+                //res_stmt = radium::Stmt::ExprS { e: drope, s: Box::new(res_stmt)};
+            },
+
+            // just a goto for our purposes
+            TerminatorKind::FalseEdge { real_target, .. }
+            // this is just a virtual edge for the borrowchecker, we can translate this to a normal goto
+            | TerminatorKind::FalseUnwind { real_target, .. } => {
+                self.translate_goto_like(&loc, *real_target)
+            },
+
+            TerminatorKind::Unreachable => Ok(radium::Stmt::Stuck),
+
+            TerminatorKind::UnwindResume => Err(TranslationError::Unimplemented {
+                description: "implement UnwindResume".to_owned(),
+            }),
+
+            TerminatorKind::UnwindTerminate(_) => Err(TranslationError::Unimplemented {
+                description: "implement UnwindTerminate".to_owned(),
+            }),
+
+            TerminatorKind::GeneratorDrop
+            | TerminatorKind::InlineAsm { .. }
+            | TerminatorKind::Yield { .. } => Err(TranslationError::UnsupportedFeature {
+                description: format!(
+                    "RefinedRust does currently not support this kind of terminator (got: {:?})",
+                    term
+                ),
+            }),
+        }
+    }
+}
diff --git a/rr_frontend/translation/src/body/translator.rs b/rr_frontend/translation/src/body/translator.rs
deleted file mode 100644
index bfb76258e500b8f8834dd9d4d29b2f5c85eda33a..0000000000000000000000000000000000000000
--- a/rr_frontend/translation/src/body/translator.rs
+++ /dev/null
@@ -1,2460 +0,0 @@
-// © 2023, The RefinedRust Developers and Contributors
-//
-// This Source Code Form is subject to the terms of the BSD-3-clause License.
-// If a copy of the BSD-3-clause license was not distributed with this
-// file, You can obtain one at https://opensource.org/license/bsd-3-clause/.
-
-use std::collections::{btree_map, BTreeMap, HashMap, HashSet};
-
-use log::{info, trace, warn};
-use radium::coq;
-use rr_rustc_interface::hir::def_id::DefId;
-use rr_rustc_interface::middle::mir::interpret::{ConstValue, ErrorHandled, Scalar};
-use rr_rustc_interface::middle::mir::tcx::PlaceTy;
-use rr_rustc_interface::middle::mir::{
-    BasicBlock, BasicBlockData, BinOp, Body, BorrowKind, Constant, ConstantKind, Local, LocalKind, Location,
-    Mutability, NonDivergingIntrinsic, Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator,
-    TerminatorKind, UnOp, VarDebugInfoContents,
-};
-use rr_rustc_interface::middle::ty::fold::TypeFolder;
-use rr_rustc_interface::middle::ty::{ConstKind, Ty, TyKind};
-use rr_rustc_interface::middle::{mir, ty};
-use rr_rustc_interface::{abi, ast, middle};
-use typed_arena::Arena;
-
-use crate::base::*;
-use crate::body::checked_op_analysis::CheckedOpLocalAnalysis;
-use crate::environment::borrowck::facts;
-use crate::environment::polonius_info::PoloniusInfo;
-use crate::environment::procedure::Procedure;
-use crate::environment::{dump_borrowck_info, polonius_info, Environment};
-use crate::regions::inclusion_tracker::InclusionTracker;
-use crate::spec_parsers::parse_utils::ParamLookup;
-use crate::spec_parsers::verbose_function_spec_parser::{
-    ClosureMetaInfo, FunctionRequirements, FunctionSpecParser, VerboseFunctionSpecParser,
-};
-use crate::traits::{registry, resolution};
-use crate::{base, consts, procedures, regions, search, traits, types};
-
-/// Get the syntypes of function arguments for a procedure call.
-pub fn get_arg_syntypes_for_procedure_call<'tcx, 'def>(
-    tcx: ty::TyCtxt<'tcx>,
-    ty_translator: &types::LocalTX<'def, 'tcx>,
-    callee_did: DefId,
-    ty_params: &[ty::GenericArg<'tcx>],
-) -> Result<Vec<radium::SynType>, TranslationError<'tcx>> {
-    let caller_did = ty_translator.get_proc_did();
-
-    // Get the type of the callee, fully instantiated
-    let full_ty: ty::EarlyBinder<Ty<'tcx>> = tcx.type_of(callee_did);
-    let full_ty = full_ty.instantiate(tcx, ty_params);
-
-    // We create a dummy scope in order to make the lifetime lookups succeed, since we only want
-    // syntactic types here.
-    // Since we do the substitution of the generics above, we should translate generics and
-    // traits in the caller's scope.
-    let scope = ty_translator.scope.borrow();
-    let param_env: ty::ParamEnv<'tcx> = tcx.param_env(scope.did);
-    let callee_state = types::CalleeState::new(&param_env, &scope.generic_scope);
-    let mut dummy_state = types::STInner::CalleeTranslation(callee_state);
-
-    let mut syntypes = Vec::new();
-    match full_ty.kind() {
-        ty::TyKind::FnDef(_, _) => {
-            let sig = full_ty.fn_sig(tcx);
-            for ty in sig.inputs().skip_binder() {
-                let st = ty_translator.translator.translate_type_to_syn_type(*ty, &mut dummy_state)?;
-                syntypes.push(st);
-            }
-        },
-        ty::TyKind::Closure(_, args) => {
-            let clos_args = args.as_closure();
-            let sig = clos_args.sig();
-            let pre_sig = sig.skip_binder();
-            // we also need to add the closure argument here
-
-            let tuple_ty = clos_args.tupled_upvars_ty();
-            match clos_args.kind() {
-                ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
-                    syntypes.push(radium::SynType::Ptr);
-                },
-                ty::ClosureKind::FnOnce => {
-                    let st =
-                        ty_translator.translator.translate_type_to_syn_type(tuple_ty, &mut dummy_state)?;
-                    syntypes.push(st);
-                },
-            }
-            for ty in pre_sig.inputs() {
-                let st = ty_translator.translator.translate_type_to_syn_type(*ty, &mut dummy_state)?;
-                syntypes.push(st);
-            }
-        },
-        _ => unimplemented!(),
-    }
-
-    Ok(syntypes)
-}
-
-/// Struct that keeps track of all information necessary to translate a MIR Body to a `radium::Function`.
-/// `'a` is the lifetime of the translator and ends after translation has finished.
-/// `'def` is the lifetime of the generated code (the code may refer to struct defs).
-/// `'tcx` is the lifetime of the rustc tctx.
-pub struct TX<'a, 'def, 'tcx> {
-    env: &'def Environment<'tcx>,
-    /// registry of other procedures
-    procedure_registry: &'a procedures::Scope<'def>,
-    /// scope of used consts
-    const_registry: &'a consts::Scope<'def>,
-    /// trait registry
-    trait_registry: &'def registry::TR<'tcx, 'def>,
-    /// translator for types
-    ty_translator: types::LocalTX<'def, 'tcx>,
-
-    /// this needs to be annotated with the right borrowck things
-    proc: &'def Procedure<'tcx>,
-    /// attributes on this function
-    attrs: &'a [&'a ast::ast::AttrItem],
-    /// polonius info for this function
-    info: &'a PoloniusInfo<'a, 'tcx>,
-
-    /// maps locals to variable names
-    variable_map: HashMap<Local, String>,
-
-    /// name of the return variable
-    return_name: String,
-    /// syntactic type of the thing to return
-    return_synty: radium::SynType,
-    /// all the other procedures used by this function, and:
-    /// (code_loc_parameter_name, spec_name, type_inst, syntype_of_all_args)
-    collected_procedures: HashMap<(DefId, types::GenericsKey<'tcx>), radium::UsedProcedure<'def>>,
-    /// used statics
-    collected_statics: HashSet<DefId>,
-
-    /// tracking lifetime inclusions for the generation of lifetime inclusions
-    inclusion_tracker: InclusionTracker<'a, 'tcx>,
-    /// initial Polonius constraints that hold at the start of the function
-    initial_constraints: Vec<(polonius_info::AtomicRegion, polonius_info::AtomicRegion)>,
-
-    /// local lifetimes: the LHS is the lifetime name, the RHS are the super lifetimes
-    local_lifetimes: Vec<(radium::specs::Lft, Vec<radium::specs::Lft>)>,
-    /// data structures for tracking which basic blocks still need to be translated
-    /// (we only translate the basic blocks which are actually reachable, in particular when
-    /// skipping unwinding)
-    bb_queue: Vec<BasicBlock>,
-    /// set of already processed blocks
-    processed_bbs: HashSet<BasicBlock>,
-
-    /// map of loop heads to their optional spec closure defid
-    loop_specs: HashMap<BasicBlock, Option<DefId>>,
-
-    /// relevant locals: (local, name, type)
-    fn_locals: Vec<(Local, String, radium::Type<'def>)>,
-
-    /// result temporaries of checked ops that we rewrite
-    /// we assume that this place is typed at (result_type, bool)
-    /// and rewrite accesses to the first component to directly use the place,
-    /// while rewriting accesses to the second component to true.
-    /// TODO: once we handle panics properly, we should use a different translation.
-    /// NOTE: we only rewrite for uses, as these are the only places these are used.
-    checked_op_temporaries: HashMap<Local, Ty<'tcx>>,
-
-    /// the Caesium function buildder
-    translated_fn: radium::FunctionBuilder<'def>,
-}
-
-impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
-    pub fn new(
-        env: &'def Environment<'tcx>,
-        procedure_registry: &'a procedures::Scope<'def>,
-        const_registry: &'a consts::Scope<'def>,
-        trait_registry: &'def registry::TR<'tcx, 'def>,
-        ty_translator: types::LocalTX<'def, 'tcx>,
-
-        proc: &'def Procedure<'tcx>,
-        attrs: &'a [&'a ast::ast::AttrItem],
-        info: &'a PoloniusInfo<'a, 'tcx>,
-        inputs: &[ty::Ty<'tcx>],
-
-        mut inclusion_tracker: InclusionTracker<'a, 'tcx>,
-        mut translated_fn: radium::FunctionBuilder<'def>,
-    ) -> Result<Self, TranslationError<'tcx>> {
-        let body = proc.get_mir();
-
-        // analyze which locals are used for the result of checked-ops, because we will
-        // override their types (eliminating the tuples)
-        let mut checked_op_analyzer = CheckedOpLocalAnalysis::new(env.tcx(), body);
-        checked_op_analyzer.analyze();
-        let checked_op_temporaries = checked_op_analyzer.results();
-
-        // map to translate between locals and the string names we use in radium::
-        let mut variable_map: HashMap<Local, String> = HashMap::new();
-
-        let local_decls = &body.local_decls;
-        info!("Have {} local decls\n", local_decls.len());
-
-        let debug_info = &body.var_debug_info;
-        info!("using debug info: {:?}", debug_info);
-
-        let mut return_synty = radium::SynType::Unit; // default
-        let mut fn_locals = Vec::new();
-        let mut opt_return_name =
-            Err(TranslationError::UnknownError("could not find local for return value".to_owned()));
-        let mut used_names = HashSet::new();
-        let mut arg_tys = Vec::new();
-
-        // go over local_decls and create the right radium:: stack layout
-        for (local, local_decl) in local_decls.iter_enumerated() {
-            let kind = body.local_kind(local);
-            let ty: Ty<'tcx>;
-            if let Some(rewritten_ty) = checked_op_temporaries.get(&local) {
-                ty = *rewritten_ty;
-            } else {
-                ty = local_decl.ty;
-            }
-
-            // check if the type is of a spec fn -- in this case, we can skip this temporary
-            if let TyKind::Closure(id, _) = ty.kind() {
-                if procedure_registry.lookup_function_mode(*id).map_or(false, procedures::Mode::is_ignore) {
-                    // this is a spec fn
-                    info!("skipping local which has specfn closure type: {:?}", local);
-                    continue;
-                }
-            }
-
-            // type:
-            info!("Trying to translate type of local {local:?}: {:?}", ty);
-            let tr_ty = ty_translator.translate_type(ty)?;
-            let st = (&tr_ty).into();
-
-            let name = Self::make_local_name(body, local, &mut used_names);
-            variable_map.insert(local, name.clone());
-
-            fn_locals.push((local, name.clone(), tr_ty));
-
-            match kind {
-                LocalKind::Arg => {
-                    translated_fn.code.add_argument(&name, st);
-                    arg_tys.push(ty);
-                },
-                //LocalKind::Var => translated_fn.code.add_local(&name, st),
-                LocalKind::Temp => translated_fn.code.add_local(&name, st),
-                LocalKind::ReturnPointer => {
-                    return_synty = st.clone();
-                    translated_fn.code.add_local(&name, st);
-                    opt_return_name = Ok(name);
-                },
-            }
-        }
-        info!("radium name map: {:?}", variable_map);
-        // create the function
-        let return_name = opt_return_name?;
-
-        // add lifetime parameters to the map
-        let initial_constraints = regions::init::get_initial_universal_arg_constraints(
-            info,
-            &mut inclusion_tracker,
-            inputs,
-            arg_tys.as_slice(),
-        );
-        info!("initial constraints: {:?}", initial_constraints);
-
-        Ok(Self {
-            env,
-            proc,
-            info,
-            variable_map,
-            translated_fn,
-            return_name,
-            return_synty,
-            inclusion_tracker,
-            collected_procedures: HashMap::new(),
-            procedure_registry,
-            attrs,
-            local_lifetimes: Vec::new(),
-            bb_queue: Vec::new(),
-            processed_bbs: HashSet::new(),
-            ty_translator,
-            loop_specs: HashMap::new(),
-            fn_locals,
-            checked_op_temporaries,
-            initial_constraints,
-            const_registry,
-            trait_registry,
-            collected_statics: HashSet::new(),
-        })
-    }
-
-    /// Generate a string identifier for a Local.
-    /// Tries to find the Rust source code name of the local, otherwise simply enumerates.
-    /// `used_names` keeps track of the Rust source code names that have already been used.
-    fn make_local_name(mir_body: &Body<'tcx>, local: Local, used_names: &mut HashSet<String>) -> String {
-        if let Some(mir_name) = Self::find_name_for_local(mir_body, local, used_names) {
-            let name = base::strip_coq_ident(&mir_name);
-            used_names.insert(mir_name);
-            name
-        } else {
-            let mut name = "__".to_owned();
-            name.push_str(&local.index().to_string());
-            name
-        }
-    }
-
-    /// Find a source name for a local of a MIR body, if possible.
-    fn find_name_for_local(
-        body: &mir::Body<'tcx>,
-        local: mir::Local,
-        used_names: &HashSet<String>,
-    ) -> Option<String> {
-        let debug_info = &body.var_debug_info;
-
-        for dbg in debug_info {
-            let name = &dbg.name;
-            let val = &dbg.value;
-            if let VarDebugInfoContents::Place(l) = *val {
-                // make sure that the place projection is empty -- otherwise this might just
-                // refer to the capture of a closure
-                if let Some(this_local) = l.as_local() {
-                    if this_local == local {
-                        // around closures, multiple symbols may be mapped to the same name.
-                        // To prevent this from happening, we check that the name hasn't been
-                        // used already
-                        // TODO: find better solution
-                        if !used_names.contains(name.as_str()) {
-                            return Some(name.as_str().to_owned());
-                        }
-                    }
-                }
-            } else {
-                // is this case used when constant propagation happens during MIR construction?
-            }
-        }
-
-        None
-    }
-
-    /// Main translation function that actually does the translation and returns a `radium::Function`
-    /// if successful.
-    pub fn translate(
-        mut self,
-        spec_arena: &'def Arena<radium::FunctionSpec<'def, radium::InnerFunctionSpec<'def>>>,
-    ) -> Result<radium::Function<'def>, TranslationError<'tcx>> {
-        // add loop info
-        let loop_info = self.proc.loop_info();
-        loop_info.dump_body_head();
-
-        // translate the function's basic blocks
-        let basic_blocks = &self.proc.get_mir().basic_blocks;
-
-        // first translate the initial basic block; we add some additional annotations to the front
-        let initial_bb_idx = BasicBlock::from_u32(0);
-        if let Some(bb) = basic_blocks.get(initial_bb_idx) {
-            let mut translated_bb = self.translate_basic_block(initial_bb_idx, bb)?;
-            // push annotation for initial constraints that relate argument's place regions to universals
-            for (r1, r2) in &self.initial_constraints {
-                translated_bb = radium::Stmt::Annot {
-                    a: radium::Annotation::CopyLftName(
-                        self.ty_translator.format_atomic_region(r1),
-                        self.ty_translator.format_atomic_region(r2),
-                    ),
-                    s: Box::new(translated_bb),
-                    why: Some("initialization".to_owned()),
-                };
-            }
-            self.translated_fn.code.add_basic_block(initial_bb_idx.as_usize(), translated_bb);
-        } else {
-            info!("No basic blocks");
-        }
-
-        while let Some(bb_idx) = self.bb_queue.pop() {
-            let bb = &basic_blocks[bb_idx];
-            let translated_bb = self.translate_basic_block(bb_idx, bb)?;
-            self.translated_fn.code.add_basic_block(bb_idx.as_usize(), translated_bb);
-        }
-
-        // assume that all generics are layoutable
-        {
-            let scope = self.ty_translator.scope.borrow();
-            for ty in scope.generic_scope.tyvars() {
-                self.translated_fn.assume_synty_layoutable(radium::SynType::Literal(ty.syn_type));
-            }
-        }
-        // assume that all used literals are layoutable
-        for g in self.ty_translator.scope.borrow().shim_uses.values() {
-            self.translated_fn.assume_synty_layoutable(g.generate_syn_type_term());
-        }
-        // assume that all used tuples are layoutable
-        for g in self.ty_translator.scope.borrow().tuple_uses.values() {
-            self.translated_fn.assume_synty_layoutable(g.generate_syn_type_term());
-        }
-
-        // TODO: process self.loop_specs
-        // - collect the relevant bb -> def_id mappings for this function (so we can eventually generate the
-        //   definition)
-        // - have a function that takes the def_id and then parses the attributes into a loop invariant
-        for (head, did) in &self.loop_specs {
-            let spec = self.parse_attributes_on_loop_spec_closure(*head, *did);
-            self.translated_fn.register_loop_invariant(head.as_usize(), spec);
-        }
-
-        // generate dependencies on other procedures.
-        for used_proc in self.collected_procedures.values() {
-            self.translated_fn.require_function(used_proc.clone());
-        }
-
-        // generate dependencies on statics
-        for did in &self.collected_statics {
-            let s = self.const_registry.get_static(*did)?;
-            self.translated_fn.require_static(s.to_owned());
-        }
-
-        Ok(self.translated_fn.into_function(spec_arena))
-    }
-}
-
-impl<'a, 'def: 'a, 'tcx: 'def> TX<'a, 'def, 'tcx> {
-    /// Internally register that we have used a procedure with a particular instantiation of generics, and
-    /// return the code parameter name.
-    /// Arguments:
-    /// - `callee_did`: the `DefId` of the callee
-    /// - `ty_params`: the instantiation for the callee's type parameters
-    /// - `trait_spec_terms`: if the callee has any trait assumptions, these are specification parameter terms
-    ///   for these traits
-    /// - `trait_assoc_tys`: if the callee has any trait assumptions, these are the instantiations for all
-    ///   associated types
-    fn register_use_procedure(
-        &mut self,
-        callee_did: DefId,
-        extra_spec_args: Vec<String>,
-        ty_params: ty::GenericArgsRef<'tcx>,
-        trait_specs: Vec<radium::TraitReqInst<'def, ty::Ty<'tcx>>>,
-    ) -> Result<(String, Vec<radium::Type<'def>>, Vec<radium::Lft>), TranslationError<'tcx>> {
-        trace!("enter register_use_procedure callee_did={callee_did:?} ty_params={ty_params:?}");
-        // The key does not include the associated types, as the resolution of the associated types
-        // should always be unique for one combination of type parameters, as long as we remain in
-        // the same environment (which is the case within this procedure).
-        // Therefore, already the type parameters are sufficient to distinguish different
-        // instantiations.
-        let key = types::generate_args_inst_key(self.env.tcx(), ty_params)?;
-
-        // re-quantify
-        let quantified_args = self.ty_translator.get_generic_abstraction_for_procedure(
-            callee_did,
-            ty_params,
-            &trait_specs,
-            true,
-        )?;
-
-        let tup = (callee_did, key);
-        let res;
-        if let Some(proc_use) = self.collected_procedures.get(&tup) {
-            res = proc_use.loc_name.clone();
-        } else {
-            let meta = self
-                .procedure_registry
-                .lookup_function(callee_did)
-                .ok_or_else(|| TranslationError::UnknownProcedure(format!("{:?}", callee_did)))?;
-            // explicit instantiation is needed sometimes
-            let spec_name = format!("{} (RRGS:=RRGS)", meta.get_spec_name());
-            let code_name = meta.get_name();
-            let loc_name = format!("{}_loc", types::mangle_name_with_tys(code_name, tup.1.as_slice()));
-
-            let syntypes = get_arg_syntypes_for_procedure_call(
-                self.env.tcx(),
-                &self.ty_translator,
-                callee_did,
-                ty_params.as_slice(),
-            )?;
-
-            let mut translated_params = quantified_args.fn_ty_param_inst;
-
-            info!(
-                "Registered procedure instance {} of {:?} with {:?} and layouts {:?}",
-                loc_name, callee_did, translated_params, syntypes
-            );
-
-            let specs = trait_specs.into_iter().map(|x| x.try_into().unwrap()).collect();
-
-            let proc_use = radium::UsedProcedure::new(
-                loc_name,
-                spec_name,
-                extra_spec_args,
-                quantified_args.scope,
-                specs,
-                translated_params,
-                quantified_args.fn_lft_param_inst,
-                syntypes,
-            );
-
-            res = proc_use.loc_name.clone();
-            self.collected_procedures.insert(tup, proc_use);
-        }
-        trace!("leave register_use_procedure");
-        Ok((res, quantified_args.callee_ty_param_inst, quantified_args.callee_lft_param_inst))
-    }
-
-    /// Internally register that we have used a trait method with a particular instantiation of
-    /// generics, and return the code parameter name.
-    fn register_use_trait_method<'c>(
-        &'c mut self,
-        callee_did: DefId,
-        ty_params: ty::GenericArgsRef<'tcx>,
-        trait_specs: Vec<radium::TraitReqInst<'def, ty::Ty<'tcx>>>,
-    ) -> Result<(String, Vec<radium::Type<'def>>, Vec<radium::Lft>), TranslationError<'tcx>> {
-        trace!("enter register_use_trait_method did={:?} ty_params={:?}", callee_did, ty_params);
-        // Does not include the associated types in the key; see `register_use_procedure` for an
-        // explanation.
-        let key = types::generate_args_inst_key(self.env.tcx(), ty_params)?;
-
-        let (method_loc_name, method_spec_term, method_params) =
-            self.ty_translator.register_use_trait_procedure(self.env, callee_did, ty_params)?;
-        // re-quantify
-        let quantified_args = self.ty_translator.get_generic_abstraction_for_procedure(
-            callee_did,
-            method_params,
-            &trait_specs,
-            false,
-        )?;
-
-        let tup = (callee_did, key);
-        let res;
-        if let Some(proc_use) = self.collected_procedures.get(&tup) {
-            res = proc_use.loc_name.clone();
-        } else {
-            // TODO: should we use ty_params or method_params?
-            let syntypes = get_arg_syntypes_for_procedure_call(
-                self.env.tcx(),
-                &self.ty_translator,
-                callee_did,
-                ty_params.as_slice(),
-            )?;
-
-            let mut translated_params = quantified_args.fn_ty_param_inst;
-
-            info!(
-                "Registered procedure instance {} of {:?} with {:?} and layouts {:?}",
-                method_loc_name, callee_did, translated_params, syntypes
-            );
-
-            let specs = trait_specs.into_iter().map(|x| x.try_into().unwrap()).collect();
-            let proc_use = radium::UsedProcedure::new(
-                method_loc_name,
-                method_spec_term,
-                vec![],
-                quantified_args.scope,
-                specs,
-                translated_params,
-                quantified_args.fn_lft_param_inst,
-                syntypes,
-            );
-
-            res = proc_use.loc_name.clone();
-            self.collected_procedures.insert(tup, proc_use);
-        }
-        trace!("leave register_use_procedure");
-        Ok((res, quantified_args.callee_ty_param_inst, quantified_args.callee_lft_param_inst))
-    }
-
-    /// Enqueues a basic block for processing, if it has not already been processed,
-    /// and marks it as having been processed.
-    fn enqueue_basic_block(&mut self, bb: BasicBlock) {
-        if !self.processed_bbs.contains(&bb) {
-            self.bb_queue.push(bb);
-            self.processed_bbs.insert(bb);
-        }
-    }
-
-    fn format_region(&self, r: facts::Region) -> String {
-        let lft = self.info.mk_atomic_region(r);
-        self.ty_translator.format_atomic_region(&lft)
-    }
-
-    /// Parse the attributes on spec closure `did` as loop annotations and add it as an invariant
-    /// to the generated code.
-    fn parse_attributes_on_loop_spec_closure(
-        &self,
-        loop_head: BasicBlock,
-        did: Option<DefId>,
-    ) -> radium::LoopSpec {
-        // for now: just make invariants True.
-
-        // need to do:
-        // - find out the locals in the right order, make parameter names for them. based on their type and
-        //   initialization status, get the refinement type.
-        // - output/pretty-print this map when generating the typing proof of each function. [done]
-        //  + should not be a separate definition, but rather a "set (.. := ...)" with a marker type so
-        //    automation can find it.
-
-        // representation of loop invariants:
-        // - introduce parameters for them.
-
-        let mut rfn_binders = Vec::new();
-        let prop_body = radium::IProp::True;
-
-        // determine invariant on initialization:
-        // - we need this both for the refinement invariant (though this could be removed if we make uninit
-        //   generic over the refinement)
-        // - in order to establish the initialization invariant in each loop iteration, since we don't have
-        //   proper subtyping for uninit => maybe we could fix this too by making uninit variant in the
-        //   refinement type? then we could have proper subtyping lemmas.
-        //  + to bring it to the right refinement type initially, maybe have some automation /
-        //  annotation
-        // TODO: consider changing it like that.
-        //
-        // Note that StorageDead will not help us for determining initialization/ making it invariant, since
-        // it only applies to full stack slots, not individual paths. one thing that makes it more
-        // complicated in the frontend: initialization may in practice also be path-dependent.
-        //  - this does not cause issues with posing a too strong loop invariant,
-        //  - but this poses an issue for annotations
-        //
-        //
-
-        // get locals
-        for (_, name, ty) in &self.fn_locals {
-            // get the refinement type
-            let mut rfn_ty = ty.get_rfn_type();
-            // wrap it in place_rfn, since we reason about places
-            rfn_ty = coq::term::Type::PlaceRfn(Box::new(rfn_ty));
-
-            // determine their initialization status
-            //let initialized = true; // TODO
-            // determine the actual refinement type for the current initialization status.
-
-            let rfn_name = format!("r_{}", name);
-            rfn_binders.push(coq::binder::Binder::new(Some(rfn_name), rfn_ty));
-        }
-
-        // TODO what do we do about stuff connecting borrows?
-        if let Some(did) = did {
-            let attrs = self.env.get_attributes(did);
-            info!("attrs for loop {:?}: {:?}", loop_head, attrs);
-        } else {
-            info!("no attrs for loop {:?}", loop_head);
-        }
-
-        let pred = radium::IPropPredicate::new(rfn_binders, prop_body);
-        radium::LoopSpec {
-            func_predicate: pred,
-        }
-    }
-
-    /// Checks whether a place access descends below a reference.
-    fn check_place_below_reference(&self, place: &Place<'tcx>) -> bool {
-        if self.checked_op_temporaries.contains_key(&place.local) {
-            // temporaries are never below references
-            return false;
-        }
-
-        for (pl, _) in place.iter_projections() {
-            // check if the current ty is a reference that we then descend under with proj
-            let cur_ty_kind = pl.ty(&self.proc.get_mir().local_decls, self.env.tcx()).ty.kind();
-            if let TyKind::Ref(_, _, _) = cur_ty_kind {
-                return true;
-            }
-        }
-
-        false
-    }
-
-    /// Split the type of a function operand of a call expression to a base type and an instantiation for
-    /// generics.
-    fn call_expr_op_split_inst(
-        &self,
-        constant: &Constant<'tcx>,
-    ) -> Result<
-        (DefId, ty::PolyFnSig<'tcx>, ty::GenericArgsRef<'tcx>, ty::PolyFnSig<'tcx>),
-        TranslationError<'tcx>,
-    > {
-        match constant.literal {
-            ConstantKind::Ty(c) => {
-                match c.ty().kind() {
-                    TyKind::FnDef(def, args) => {
-                        let ty: ty::EarlyBinder<Ty<'tcx>> = self.env.tcx().type_of(def);
-                        let ty_ident = ty.instantiate_identity();
-                        assert!(ty_ident.is_fn());
-                        let ident_sig = ty_ident.fn_sig(self.env.tcx());
-
-                        let ty_instantiated = ty.instantiate(self.env.tcx(), args.as_slice());
-                        let instantiated_sig = ty_instantiated.fn_sig(self.env.tcx());
-
-                        Ok((*def, ident_sig, args, instantiated_sig))
-                    },
-                    // TODO handle FnPtr, closure
-                    _ => Err(TranslationError::Unimplemented {
-                        description: "implement function pointers".to_owned(),
-                    }),
-                }
-            },
-            ConstantKind::Val(_, ty) => {
-                match ty.kind() {
-                    TyKind::FnDef(def, args) => {
-                        let ty: ty::EarlyBinder<Ty<'tcx>> = self.env.tcx().type_of(def);
-
-                        let ty_ident = ty.instantiate_identity();
-                        assert!(ty_ident.is_fn());
-                        let ident_sig = ty_ident.fn_sig(self.env.tcx());
-
-                        let ty_instantiated = ty.instantiate(self.env.tcx(), args.as_slice());
-                        let instantiated_sig = ty_instantiated.fn_sig(self.env.tcx());
-
-                        Ok((*def, ident_sig, args, instantiated_sig))
-                    },
-                    // TODO handle FnPtr, closure
-                    _ => Err(TranslationError::Unimplemented {
-                        description: "implement function pointers".to_owned(),
-                    }),
-                }
-            },
-            ConstantKind::Unevaluated(_, _) => Err(TranslationError::Unimplemented {
-                description: "implement ConstantKind::Unevaluated".to_owned(),
-            }),
-        }
-    }
-
-    /// Find the optional `DefId` of the closure giving the invariant for the loop with head `head_bb`.
-    fn find_loop_spec_closure(&self, head_bb: BasicBlock) -> Result<Option<DefId>, TranslationError<'tcx>> {
-        let bodies = self.proc.loop_info().get_loop_body(head_bb);
-        let basic_blocks = &self.proc.get_mir().basic_blocks;
-
-        // we go in order through the bodies in order to not stumble upon an annotation for a
-        // nested loop!
-        for body in bodies {
-            // check that we did not go below a nested loop
-            if self.proc.loop_info().get_loop_head(*body) == Some(head_bb) {
-                // check the statements for an assignment
-                let data = basic_blocks.get(*body).unwrap();
-                for stmt in &data.statements {
-                    if let StatementKind::Assign(box (pl, _)) = stmt.kind {
-                        if let Some(did) = self.is_spec_closure_local(pl.local)? {
-                            return Ok(Some(did));
-                        }
-                    }
-                }
-            }
-        }
-
-        Ok(None)
-    }
-
-    /// Translate a goto-like jump to `target`.
-    fn translate_goto_like(
-        &mut self,
-        _loc: &Location,
-        target: BasicBlock,
-    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
-        self.enqueue_basic_block(target);
-        let res_stmt = radium::Stmt::GotoBlock(target.as_usize());
-
-        let loop_info = self.proc.loop_info();
-        if loop_info.is_loop_head(target) && !self.loop_specs.contains_key(&target) {
-            let spec_defid = self.find_loop_spec_closure(target)?;
-            self.loop_specs.insert(target, spec_defid);
-        }
-
-        Ok(res_stmt)
-    }
-
-    /// Check if a call goes to `std::rt::begin_panic`
-    fn is_call_destination_panic(&mut self, func: &Operand) -> bool {
-        let Operand::Constant(box c) = func else {
-            return false;
-        };
-
-        let ConstantKind::Val(_, ty) = c.literal else {
-            return false;
-        };
-
-        let TyKind::FnDef(did, _) = ty.kind() else {
-            return false;
-        };
-
-        if let Some(panic_id_std) =
-            search::try_resolve_did(self.env.tcx(), &["std", "panicking", "begin_panic"])
-        {
-            if panic_id_std == *did {
-                return true;
-            }
-        } else {
-            warn!("Failed to determine DefId of std::panicking::begin_panic");
-        }
-
-        if let Some(panic_id_core) = search::try_resolve_did(self.env.tcx(), &["core", "panicking", "panic"])
-        {
-            if panic_id_core == *did {
-                return true;
-            }
-        } else {
-            warn!("Failed to determine DefId of core::panicking::panic");
-        }
-
-        false
-    }
-
-    /// Registers a drop shim for a particular type for the translation.
-    #[allow(clippy::unused_self)]
-    const fn register_drop_shim_for(&self, _ty: Ty<'tcx>) {
-        // TODO!
-        //let drop_in_place_did: DefId = search::try_resolve_did(self.env.tcx(), &["std", "ptr",
-        // "drop_in_place"]).unwrap();
-
-        //let x: ty::InstanceDef = ty::InstanceDef::DropGlue(drop_in_place_did, Some(ty));
-        //let body: &'tcx mir::Body = self.env.tcx().mir_shims(x);
-
-        //info!("Generated drop shim for {:?}", ty);
-        //Self::dump_body(body);
-    }
-
-    fn translate_function_call(
-        &mut self,
-        func: &Operand<'tcx>,
-        args: &[Operand<'tcx>],
-        destination: &Place<'tcx>,
-        target: Option<middle::mir::BasicBlock>,
-        loc: Location,
-        dying_loans: &[facts::Loan],
-    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
-        let startpoint = self.info.interner.get_point_index(&facts::Point {
-            location: loc,
-            typ: facts::PointType::Start,
-        });
-
-        let Operand::Constant(box func_constant) = func else {
-            return Err(TranslationError::UnsupportedFeature {
-                description: format!(
-                    "RefinedRust does currently not support this kind of call operand (got: {:?})",
-                    func
-                ),
-            });
-        };
-
-        // Get the type of the return value from the function
-        let (target_did, sig, generic_args, inst_sig) = self.call_expr_op_split_inst(func_constant)?;
-        info!("calling function {:?}", target_did);
-        info!("call substs: {:?} = {:?}, {:?}", func, sig, generic_args);
-
-        // for lifetime annotations:
-        // 1. get the regions involved here. for that, get the instantiation of the function.
-        //    + if it's a FnDef type, that should be easy.
-        //    + for a function pointer: ?
-        //    + for a closure: ?
-        //   (Polonius does not seem to distinguish early/late bound in any way, except
-        //   that they are numbered in different passes)
-        // 2. find the constraints here involving the regions.
-        // 3. solve for the regions.
-        //    + transitively propagate the constraints
-        //    + check for equalities
-        //    + otherwise compute intersection. singleton intersection just becomes an equality def.
-        // 4. add annotations accordingly
-        //    + either a startlft
-        //    + or a copy name
-        // 5. add shortenlft annotations to line up arguments.
-        //    + for that, we need the type of the LHS, and what the argument types (with
-        //    substituted regions) should be.
-        // 6. annotate the return value on assignment and establish constraints.
-
-        let classification =
-            regions::calls::compute_call_regions(self.env, &self.inclusion_tracker, generic_args, loc);
-
-        // update the inclusion tracker with the new regions we have introduced
-        // We just add the inclusions and ignore that we resolve it in a "tight" way.
-        // the cases where we need the reverse inclusion should be really rare.
-        for (r, c) in &classification.classification {
-            match c {
-                regions::calls::CallRegionKind::EqR(r2) => {
-                    // put it at the start point, because the inclusions come into effect
-                    // at the point right before.
-                    self.inclusion_tracker.add_static_inclusion(*r, *r2, startpoint);
-                    self.inclusion_tracker.add_static_inclusion(*r2, *r, startpoint);
-                },
-                regions::calls::CallRegionKind::Intersection(lfts) => {
-                    // all the regions represented by lfts need to be included in r
-                    for r2 in lfts {
-                        self.inclusion_tracker.add_static_inclusion(*r2, *r, startpoint);
-                    }
-                },
-            }
-        }
-
-        // translate the function expression.
-        let func_expr = self.translate_operand(func, false)?;
-        // We expect this to be an Expr::CallTarget, being annotated with the type parameters we
-        // instantiate it with.
-        let radium::Expr::CallTarget(func_lit, ty_param_annots, mut lft_param_annots) = func_expr else {
-            unreachable!("Logic error in call target translation");
-        };
-        let func_expr = radium::Expr::MetaParam(func_lit);
-
-        // translate the arguments
-        let mut translated_args = Vec::new();
-        for arg in args {
-            // to_ty is the type the function expects
-
-            //let ty = arg.ty(&self.proc.get_mir().local_decls, self.env.tcx());
-            let translated_arg = self.translate_operand(arg, true)?;
-            translated_args.push(translated_arg);
-        }
-
-        // We have to add the late regions, since we do not requantify over them.
-        for late in &classification.late_regions {
-            let lft = self.format_region(*late);
-            lft_param_annots.push(lft);
-        }
-        info!("Call lifetime instantiation (early): {:?}", classification.early_regions);
-        info!("Call lifetime instantiation (late): {:?}", classification.late_regions);
-
-        // TODO: do we need to do something with late bounds?
-        let output_ty = inst_sig.output().skip_binder();
-        info!("call has instantiated type {:?}", inst_sig);
-
-        // compute the resulting annotations
-        let lhs_ty = self.get_type_of_place(destination);
-        let lhs_strongly_writeable = !self.check_place_below_reference(destination);
-        let (rhs_annots, pre_stmt_annots, post_stmt_annots) = regions::assignment::get_assignment_annots(
-            self.env,
-            &mut self.inclusion_tracker,
-            &self.ty_translator,
-            loc,
-            lhs_strongly_writeable,
-            lhs_ty,
-            output_ty,
-        );
-        info!(
-            "assignment annots after call: expr: {:?}, pre-stmt: {:?}, post-stmt: {:?}",
-            rhs_annots, pre_stmt_annots, post_stmt_annots
-        );
-
-        // TODO: add annotations for the assignment
-        // for that:
-        // - get the type of the place
-        // - enforce constraints as necessary. this might spawn dyninclusions with some of the new regions =>
-        //   In Coq, also the aliases should get proper endlft events to resolve the dyninclusions.
-        // - update the name map
-        let call_expr = radium::Expr::Call {
-            f: Box::new(func_expr),
-            lfts: lft_param_annots,
-            tys: ty_param_annots,
-            args: translated_args,
-        };
-        let stmt = match target {
-            Some(target) => {
-                let mut cont_stmt = self.translate_goto_like(&loc, target)?;
-                // end loans before the goto, but after the call.
-                // TODO: may cause duplications?
-                cont_stmt = self.prepend_endlfts(cont_stmt, dying_loans.iter().copied());
-
-                let cont_stmt = radium::Stmt::with_annotations(
-                    cont_stmt,
-                    post_stmt_annots,
-                    &Some("post_function_call".to_owned()),
-                );
-
-                // assign stmt with call; then jump to bb
-                let place_ty = self.get_type_of_place(destination);
-                let place_st = self.ty_translator.translate_type_to_syn_type(place_ty.ty)?;
-                let place_expr = self.translate_place(destination)?;
-                let ot = place_st.into();
-
-                let annotated_rhs = radium::Expr::with_optional_annotation(
-                    call_expr,
-                    rhs_annots,
-                    Some("function_call".to_owned()),
-                );
-                let assign_stmt = radium::Stmt::Assign {
-                    ot,
-                    e1: place_expr,
-                    e2: annotated_rhs,
-                    s: Box::new(cont_stmt),
-                };
-                radium::Stmt::with_annotations(
-                    assign_stmt,
-                    pre_stmt_annots,
-                    &Some("function_call".to_owned()),
-                )
-            },
-            None => {
-                // expr stmt with call; then stuck (we have not provided a continuation, after all)
-                radium::Stmt::ExprS {
-                    e: call_expr,
-                    s: Box::new(radium::Stmt::Stuck),
-                }
-            },
-        };
-
-        let mut stmt_annots = Vec::new();
-
-        // add annotations to initialize the regions for the call (before the call)
-        for (r, class) in &classification.classification {
-            let lft = self.format_region(*r);
-            match class {
-                regions::calls::CallRegionKind::EqR(r2) => {
-                    let lft2 = self.format_region(*r2);
-                    stmt_annots.push(radium::Annotation::CopyLftName(lft2, lft));
-                },
-
-                regions::calls::CallRegionKind::Intersection(rs) => {
-                    match rs.len() {
-                        0 => {
-                            return Err(TranslationError::UnsupportedFeature {
-                                description: "RefinedRust does currently not support unconstrained lifetime"
-                                    .to_owned(),
-                            });
-                        },
-                        1 => {
-                            // this is really just an equality constraint
-                            if let Some(r2) = rs.iter().next() {
-                                let lft2 = self.format_region(*r2);
-                                stmt_annots.push(radium::Annotation::CopyLftName(lft2, lft));
-                            }
-                        },
-                        _ => {
-                            // a proper intersection
-                            let lfts: Vec<_> = rs.iter().map(|r| self.format_region(*r)).collect();
-                            stmt_annots.push(radium::Annotation::AliasLftIntersection(lft, lfts));
-                        },
-                    };
-                },
-            }
-        }
-
-        let stmt = radium::Stmt::with_annotations(stmt, stmt_annots, &Some("function_call".to_owned()));
-        Ok(stmt)
-    }
-
-    /// Translate a terminator.
-    /// We pass the dying loans during this terminator. They need to be added at the right
-    /// intermediate point.
-    fn translate_terminator(
-        &mut self,
-        term: &Terminator<'tcx>,
-        loc: Location,
-        dying_loans: Vec<facts::Loan>,
-    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
-        match &term.kind {
-            TerminatorKind::Goto { target } => self.translate_goto_like(&loc, *target),
-
-            TerminatorKind::Call {
-                func,
-                args,
-                destination,
-                target,
-                ..
-            } => {
-                trace!("translating Call {:?}", term);
-                if self.is_call_destination_panic(func) {
-                    info!("Replacing call to std::panicking::begin_panic with Stuck");
-                    return Ok(radium::Stmt::Stuck);
-                }
-
-                self.translate_function_call(func, args, destination, *target, loc, dying_loans.as_slice())
-            },
-
-            TerminatorKind::Return => {
-                // TODO: this requires additional handling for reborrows
-
-                // read from the return place
-                // Is this semantics accurate wrt what the intended MIR semantics is?
-                // Possibly handle this differently by making the first argument of a function a dedicated
-                // return place? See also discussion at https://github.com/rust-lang/rust/issues/71117
-                let stmt = radium::Stmt::Return(radium::Expr::Use {
-                    ot: (&self.return_synty).into(),
-                    e: Box::new(radium::Expr::Var(self.return_name.clone())),
-                });
-
-                // TODO is this right?
-                Ok(self.prepend_endlfts(stmt, dying_loans.into_iter()))
-            },
-
-            //TerminatorKind::Abort => {
-            //res_stmt = radium::Stmt::Stuck;
-            //res_stmt = self.prepend_endlfts(res_stmt, dying_loans.into_iter());
-            //},
-            TerminatorKind::SwitchInt { discr, targets } => {
-                let operand = self.translate_operand(discr, true)?;
-                let all_targets: &[BasicBlock] = targets.all_targets();
-
-                if self.get_type_of_operand(discr).is_bool() {
-                    // we currently special-case this as Caesium has a built-in if and this is more
-                    // convenient to handle for the type-checker
-
-                    // implementation detail: the first index is the `false` branch, the second the
-                    // `true` branch
-                    let true_target = all_targets[1];
-                    let false_target = all_targets[0];
-
-                    let true_branch = self.translate_goto_like(&loc, true_target)?;
-                    let false_branch = self.translate_goto_like(&loc, false_target)?;
-
-                    let stmt = radium::Stmt::If {
-                        e: operand,
-                        ot: radium::OpType::Bool,
-                        s1: Box::new(true_branch),
-                        s2: Box::new(false_branch),
-                    };
-
-                    // TODO: is this right?
-                    return Ok(self.prepend_endlfts(stmt, dying_loans.into_iter()));
-                }
-
-                //info!("switchint: {:?}", term.kind);
-                let operand = self.translate_operand(discr, true)?;
-                let ty = self.get_type_of_operand(discr);
-
-                let mut target_map: HashMap<u128, usize> = HashMap::new();
-                let mut translated_targets: Vec<radium::Stmt> = Vec::new();
-
-                for (idx, (tgt, bb)) in targets.iter().enumerate() {
-                    let bb: BasicBlock = bb;
-                    let translated_target = self.translate_goto_like(&loc, bb)?;
-
-                    target_map.insert(tgt, idx);
-                    translated_targets.push(translated_target);
-                }
-
-                let translated_default = self.translate_goto_like(&loc, targets.otherwise())?;
-                // TODO: need to put endlfts infront of gotos?
-
-                let translated_ty = self.ty_translator.translate_type(ty)?;
-                let radium::Type::Int(it) = translated_ty else {
-                    return Err(TranslationError::UnknownError(
-                        "SwitchInt switching on non-integer type".to_owned(),
-                    ));
-                };
-
-                Ok(radium::Stmt::Switch {
-                    e: operand,
-                    it,
-                    index_map: target_map,
-                    bs: translated_targets,
-                    def: Box::new(translated_default),
-                })
-            },
-
-            TerminatorKind::Assert {
-                cond,
-                expected,
-                target,
-                ..
-            } => {
-                // this translation gets stuck on failure
-                let cond_translated = self.translate_operand(cond, true)?;
-                let comp = radium::Expr::BinOp {
-                    o: radium::Binop::Eq,
-                    ot1: radium::OpType::Bool,
-                    ot2: radium::OpType::Bool,
-                    e1: Box::new(cond_translated),
-                    e2: Box::new(radium::Expr::Literal(radium::Literal::Bool(*expected))),
-                };
-
-                let stmt = self.translate_goto_like(&loc, *target)?;
-
-                // TODO: should we really have this?
-                let stmt = self.prepend_endlfts(stmt, dying_loans.into_iter());
-
-                Ok(radium::Stmt::AssertS {
-                    e: comp,
-                    s: Box::new(stmt),
-                })
-            },
-
-            TerminatorKind::Drop { place, target, .. } => {
-                let ty = self.get_type_of_place(place);
-                self.register_drop_shim_for(ty.ty);
-
-                let place_translated = self.translate_place(place)?;
-                let _drope = radium::Expr::DropE(Box::new(place_translated));
-
-                let stmt = self.translate_goto_like(&loc, *target)?;
-
-                Ok(self.prepend_endlfts(stmt, dying_loans.into_iter()))
-
-                //res_stmt = radium::Stmt::ExprS { e: drope, s: Box::new(res_stmt)};
-            },
-
-            // just a goto for our purposes
-            TerminatorKind::FalseEdge { real_target, .. }
-            // this is just a virtual edge for the borrowchecker, we can translate this to a normal goto
-            | TerminatorKind::FalseUnwind { real_target, .. } => {
-                self.translate_goto_like(&loc, *real_target)
-            },
-
-            TerminatorKind::Unreachable => Ok(radium::Stmt::Stuck),
-
-            TerminatorKind::UnwindResume => Err(TranslationError::Unimplemented {
-                description: "implement UnwindResume".to_owned(),
-            }),
-
-            TerminatorKind::UnwindTerminate(_) => Err(TranslationError::Unimplemented {
-                description: "implement UnwindTerminate".to_owned(),
-            }),
-
-            TerminatorKind::GeneratorDrop
-            | TerminatorKind::InlineAsm { .. }
-            | TerminatorKind::Yield { .. } => Err(TranslationError::UnsupportedFeature {
-                description: format!(
-                    "RefinedRust does currently not support this kind of terminator (got: {:?})",
-                    term
-                ),
-            }),
-        }
-    }
-
-    /// Prepend endlft annotations for dying loans to a statement.
-    fn prepend_endlfts<I>(&self, st: radium::Stmt, dying: I) -> radium::Stmt
-    where
-        I: ExactSizeIterator<Item = facts::Loan>,
-    {
-        let mut cont_stmt = st;
-        if dying.len() > 0 {
-            //info!("Dying at {:?}: {:?}", loc, dying);
-            for d in dying {
-                let lft = self.info.atomic_region_of_loan(d);
-                cont_stmt = radium::Stmt::Annot {
-                    a: radium::Annotation::EndLft(self.ty_translator.format_atomic_region(&lft)),
-                    s: Box::new(cont_stmt),
-                    why: Some("endlft".to_owned()),
-                };
-            }
-        }
-        cont_stmt
-    }
-
-    /// Get predecessors in the CFG.
-    fn get_loc_predecessors(&self, loc: Location) -> Vec<Location> {
-        if loc.statement_index > 0 {
-            let pred = Location {
-                block: loc.block,
-                statement_index: loc.statement_index - 1,
-            };
-            vec![pred]
-        } else {
-            // check for gotos that go to this basic block
-            let pred_bbs = self.proc.predecessors(loc.block);
-            let basic_blocks = &self.proc.get_mir().basic_blocks;
-            pred_bbs
-                .iter()
-                .map(|bb| {
-                    let data = &basic_blocks[*bb];
-                    Location {
-                        block: *bb,
-                        statement_index: data.statements.len(),
-                    }
-                })
-                .collect()
-        }
-    }
-
-    /// Check if a local is used for a spec closure.
-    fn is_spec_closure_local(&self, l: Local) -> Result<Option<DefId>, TranslationError<'tcx>> {
-        // check if we should ignore this
-        let local_type = self.get_type_of_local(l)?;
-
-        let TyKind::Closure(did, _) = local_type.kind() else {
-            return Ok(None);
-        };
-
-        Ok(self
-            .procedure_registry
-            .lookup_function_mode(*did)
-            .and_then(|m| m.is_ignore().then_some(*did)))
-    }
-
-    /**
-     * Translate a single basic block.
-     */
-    fn translate_basic_block(
-        &mut self,
-        bb_idx: BasicBlock,
-        bb: &BasicBlockData<'tcx>,
-    ) -> Result<radium::Stmt, TranslationError<'tcx>> {
-        // we translate from back to front, starting with the terminator, since Caesium statements
-        // have a continuation (the next statement to execute)
-
-        // first do the endlfts for the things right before the terminator
-        let mut idx = bb.statements.len();
-        let loc = Location {
-            block: bb_idx,
-            statement_index: idx,
-        };
-        let dying = self.info.get_dying_loans(loc);
-        // TODO zombie?
-        let _dying_zombie = self.info.get_dying_zombie_loans(loc);
-        let mut cont_stmt: radium::Stmt = self.translate_terminator(bb.terminator(), loc, dying)?;
-
-        //cont_stmt = self.prepend_endlfts(cont_stmt, loc, dying);
-        //cont_stmt = self.prepend_endlfts(cont_stmt, loc, dying_zombie);
-
-        for stmt in bb.statements.iter().rev() {
-            idx -= 1;
-            let loc = Location {
-                block: bb_idx,
-                statement_index: idx,
-            };
-
-            // get all dying loans, and emit endlfts for these.
-            // We loop over all predecessor locations, since some loans may end at the start of a
-            // basic block (in particular related to NLL stuff)
-            let pred = self.get_loc_predecessors(loc);
-            let mut dying_loans = HashSet::new();
-            for p in pred {
-                let dying_between = self.info.get_loans_dying_between(p, loc, false);
-                for l in &dying_between {
-                    dying_loans.insert(*l);
-                }
-                // also include zombies
-                let dying_between = self.info.get_loans_dying_between(p, loc, true);
-                for l in &dying_between {
-                    dying_loans.insert(*l);
-                }
-            }
-            // we prepend them before the current statement
-
-            match &stmt.kind {
-                StatementKind::Assign(b) => {
-                    let (plc, val) = b.as_ref();
-
-                    if (self.is_spec_closure_local(plc.local)?).is_some() {
-                        info!("skipping assignment to spec closure local: {:?}", plc);
-                    } else if let Some(rewritten_ty) = self.checked_op_temporaries.get(&plc.local) {
-                        // if this is a checked op, be sure to remember it
-                        info!("rewriting assignment to checked op: {:?}", plc);
-
-                        let synty = self.ty_translator.translate_type_to_syn_type(*rewritten_ty)?;
-
-                        let translated_val = self.translate_rvalue(loc, val)?;
-                        let translated_place = self.translate_place(plc)?;
-
-                        // this should be a temporary
-                        assert!(plc.projection.is_empty());
-
-                        let ot = synty.into();
-                        cont_stmt = radium::Stmt::Assign {
-                            ot,
-                            e1: translated_place,
-                            e2: translated_val,
-                            s: Box::new(cont_stmt),
-                        };
-                    } else {
-                        let plc_ty = self.get_type_of_place(plc);
-                        let rhs_ty = val.ty(&self.proc.get_mir().local_decls, self.env.tcx());
-
-                        let borrow_annots = regions::assignment::get_assignment_loan_annots(
-                            &mut self.inclusion_tracker, &self.ty_translator,
-                            loc, val);
-
-                        let plc_ty = self.get_type_of_place(plc);
-                        let plc_strongly_writeable = !self.check_place_below_reference(plc);
-                        let (expr_annot, pre_stmt_annots, post_stmt_annots) =
-                            regions::assignment::get_assignment_annots(
-                                self.env, &mut self.inclusion_tracker, &self.ty_translator,
-                                loc, plc_strongly_writeable, plc_ty, rhs_ty);
-
-                        // TODO; maybe move this to rvalue
-                        let composite_annots = regions::composite::get_composite_rvalue_creation_annots(
-                            self.env, &mut self.inclusion_tracker, &self.ty_translator, loc, rhs_ty);
-
-                        cont_stmt = radium::Stmt::with_annotations(
-                            cont_stmt,
-                            post_stmt_annots,
-                            &Some("post-assignment".to_owned()),
-                        );
-
-                        let translated_val = radium::Expr::with_optional_annotation(
-                            self.translate_rvalue(loc, val)?,
-                            expr_annot,
-                            Some("assignment".to_owned()),
-                        );
-                        let translated_place = self.translate_place(plc)?;
-                        let synty = self.ty_translator.translate_type_to_syn_type(plc_ty.ty)?;
-                        cont_stmt = radium::Stmt::Assign {
-                            ot: synty.into(),
-                            e1: translated_place,
-                            e2: translated_val,
-                            s: Box::new(cont_stmt),
-                        };
-                        cont_stmt = radium::Stmt::with_annotations(
-                            cont_stmt,
-                            pre_stmt_annots,
-                            &Some("assignment".to_owned()),
-                        );
-                        cont_stmt = radium::Stmt::with_annotations(
-                            cont_stmt,
-                            borrow_annots,
-                            &Some("borrow".to_owned()),
-                        );
-                        cont_stmt = radium::Stmt::with_annotations(
-                            cont_stmt,
-                            composite_annots,
-                            &Some("composite".to_owned()),
-                        );
-                    }
-                },
-
-                StatementKind::Deinit(_) => {
-                    // TODO: find out where this is emitted
-                    return Err(TranslationError::UnsupportedFeature {
-                        description: "RefinedRust does currently not support Deinit".to_owned(),
-                    });
-                },
-
-                StatementKind::FakeRead(b) => {
-                    // we can probably ignore this, but I'm not sure
-                    info!("Ignoring FakeRead: {:?}", b);
-                },
-
-                StatementKind::Intrinsic(intrinsic) => {
-                    match intrinsic.as_ref() {
-                        NonDivergingIntrinsic::Assume(_) => {
-                            // ignore
-                            info!("Ignoring Assume: {:?}", intrinsic);
-                        },
-                        NonDivergingIntrinsic::CopyNonOverlapping(_) => {
-                            return Err(TranslationError::UnsupportedFeature {
-                                description: "RefinedRust does currently not support the CopyNonOverlapping Intrinsic".to_owned(),
-                            });
-                        },
-                    }
-                },
-
-                StatementKind::PlaceMention(place) => {
-                    // TODO: this is missed UB
-                    info!("Ignoring PlaceMention: {:?}", place);
-                },
-
-                StatementKind::SetDiscriminant {
-                    place: _place,
-                    variant_index: _variant_index,
-                } => {
-                    // TODO
-                    return Err(TranslationError::UnsupportedFeature {
-                        description: "RefinedRust does currently not support SetDiscriminant".to_owned(),
-                    });
-                },
-
-                // don't need that info
-                | StatementKind::AscribeUserType(_, _)
-                // don't need that
-                | StatementKind::Coverage(_)
-                // no-op
-                | StatementKind::ConstEvalCounter
-                // ignore
-                | StatementKind::Nop
-                // just ignore
-                | StatementKind::StorageLive(_)
-                // just ignore
-                | StatementKind::StorageDead(_)
-                // just ignore retags
-                | StatementKind::Retag(_, _) => (),
-            }
-
-            cont_stmt = self.prepend_endlfts(cont_stmt, dying_loans.into_iter());
-        }
-
-        Ok(cont_stmt)
-    }
-
-    /// Translate a `BorrowKind`.
-    fn translate_borrow_kind(kind: BorrowKind) -> Result<radium::BorKind, TranslationError<'tcx>> {
-        match kind {
-            BorrowKind::Shared => Ok(radium::BorKind::Shared),
-            BorrowKind::Shallow => {
-                // TODO: figure out what to do with this
-                // arises in match lowering
-                Err(TranslationError::UnsupportedFeature {
-                    description: "RefinedRust does currently not support shallow borrows".to_owned(),
-                })
-            },
-            BorrowKind::Mut { .. } => {
-                // TODO: handle two-phase borrows?
-                Ok(radium::BorKind::Mutable)
-            },
-        }
-    }
-
-    const fn translate_mutability(mt: Mutability) -> radium::Mutability {
-        match mt {
-            Mutability::Mut => radium::Mutability::Mut,
-            Mutability::Not => radium::Mutability::Shared,
-        }
-    }
-
-    /// Get the inner type of a type to which we can apply the offset operator.
-    fn get_offset_ty(ty: Ty<'tcx>) -> Result<Ty<'tcx>, TranslationError<'tcx>> {
-        match ty.kind() {
-            TyKind::Array(t, _) | TyKind::Slice(t) | TyKind::Ref(_, t, _) => Ok(*t),
-            TyKind::RawPtr(tm) => Ok(tm.ty),
-            _ => Err(TranslationError::UnknownError(format!("cannot take offset of {}", ty))),
-        }
-    }
-
-    /// Translate binary operators.
-    /// We need access to the operands, too, to handle the offset operator and get the right
-    /// Caesium layout annotation.
-    fn translate_binop(
-        &self,
-        op: BinOp,
-        e1: &Operand<'tcx>,
-        _e2: &Operand<'tcx>,
-    ) -> Result<radium::Binop, TranslationError<'tcx>> {
-        match op {
-            BinOp::Add | BinOp::AddUnchecked => Ok(radium::Binop::Add),
-            BinOp::Sub | BinOp::SubUnchecked => Ok(radium::Binop::Sub),
-            BinOp::Mul | BinOp::MulUnchecked => Ok(radium::Binop::Mul),
-            BinOp::Div => Ok(radium::Binop::Div),
-            BinOp::Rem => Ok(radium::Binop::Mod),
-
-            BinOp::BitXor => Ok(radium::Binop::BitXor),
-            BinOp::BitAnd => Ok(radium::Binop::BitAnd),
-            BinOp::BitOr => Ok(radium::Binop::BitOr),
-            BinOp::Shl | BinOp::ShlUnchecked => Ok(radium::Binop::Shl),
-            BinOp::Shr | BinOp::ShrUnchecked => Ok(radium::Binop::Shr),
-
-            BinOp::Eq => Ok(radium::Binop::Eq),
-            BinOp::Lt => Ok(radium::Binop::Lt),
-            BinOp::Le => Ok(radium::Binop::Le),
-            BinOp::Ne => Ok(radium::Binop::Ne),
-            BinOp::Ge => Ok(radium::Binop::Ge),
-            BinOp::Gt => Ok(radium::Binop::Gt),
-
-            BinOp::Offset => {
-                // we need to get the layout of the thing we're offsetting
-                // try to get the type of e1.
-                let e1_ty = self.get_type_of_operand(e1);
-                let off_ty = TX::get_offset_ty(e1_ty)?;
-                let st = self.ty_translator.translate_type_to_syn_type(off_ty)?;
-                let ly = st.into();
-                Ok(radium::Binop::PtrOffset(ly))
-            },
-        }
-    }
-
-    /// Translate checked binary operators.
-    /// We need access to the operands, too, to handle the offset operator and get the right
-    /// Caesium layout annotation.
-    fn translate_checked_binop(op: BinOp) -> Result<radium::Binop, TranslationError<'tcx>> {
-        match op {
-            BinOp::Add => Ok(radium::Binop::CheckedAdd),
-            BinOp::Sub => Ok(radium::Binop::CheckedSub),
-            BinOp::Mul => Ok(radium::Binop::CheckedMul),
-            BinOp::Shl => Err(TranslationError::UnsupportedFeature {
-                description: "RefinedRust does currently not support checked Shl".to_owned(),
-            }),
-            BinOp::Shr => Err(TranslationError::UnsupportedFeature {
-                description: "RefinedRust does currently not support checked Shr".to_owned(),
-            }),
-            _ => Err(TranslationError::UnknownError(
-                "unexpected checked binop that is not Add, Sub, Mul, Shl, or Shr".to_owned(),
-            )),
-        }
-    }
-
-    /// Translate unary operators.
-    fn translate_unop(op: UnOp, ty: Ty<'tcx>) -> Result<radium::Unop, TranslationError<'tcx>> {
-        match op {
-            UnOp::Not => match ty.kind() {
-                ty::TyKind::Bool => Ok(radium::Unop::NotBool),
-                ty::TyKind::Int(_) | ty::TyKind::Uint(_) => Ok(radium::Unop::NotInt),
-                _ => Err(TranslationError::UnknownError(
-                    "application of UnOp::Not to non-{Int, Bool}".to_owned(),
-                )),
-            },
-            UnOp::Neg => Ok(radium::Unop::Neg),
-        }
-    }
-
-    /// Get the type to annotate a borrow with.
-    fn get_type_annotation_for_borrow(
-        &self,
-        bk: BorrowKind,
-        pl: &Place<'tcx>,
-    ) -> Result<Option<radium::RustType>, TranslationError<'tcx>> {
-        let BorrowKind::Mut { .. } = bk else {
-            return Ok(None);
-        };
-
-        let ty = self.get_type_of_place(pl);
-
-        // For borrows, we can safely ignore the downcast type -- we cannot borrow a particularly variant
-        let translated_ty = self.ty_translator.translate_type(ty.ty)?;
-        let annot_ty = radium::RustType::of_type(&translated_ty);
-
-        Ok(Some(annot_ty))
-    }
-
-    /// Translates an Rvalue.
-    fn translate_rvalue(
-        &mut self,
-        loc: Location,
-        rval: &Rvalue<'tcx>,
-    ) -> Result<radium::Expr, TranslationError<'tcx>> {
-        match rval {
-            Rvalue::Use(op) => {
-                // converts an lvalue to an rvalue
-                self.translate_operand(op, true)
-            },
-
-            Rvalue::Ref(region, bk, pl) => {
-                let translated_pl = self.translate_place(pl)?;
-                let translated_bk = TX::translate_borrow_kind(*bk)?;
-                let ty_annot = self.get_type_annotation_for_borrow(*bk, pl)?;
-
-                if let Some(loan) = self.info.get_optional_loan_at_location(loc) {
-                    let atomic_region = self.info.atomic_region_of_loan(loan);
-                    let lft = self.ty_translator.format_atomic_region(&atomic_region);
-                    Ok(radium::Expr::Borrow {
-                        lft,
-                        bk: translated_bk,
-                        ty: ty_annot,
-                        e: Box::new(translated_pl),
-                    })
-                } else {
-                    info!("Didn't find loan at {:?}: {:?}; region {:?}", loc, rval, region);
-                    let region = regions::region_to_region_vid(*region);
-                    let lft = self.format_region(region);
-
-                    Ok(radium::Expr::Borrow {
-                        lft,
-                        bk: translated_bk,
-                        ty: ty_annot,
-                        e: Box::new(translated_pl),
-                    })
-                }
-            },
-
-            Rvalue::AddressOf(mt, pl) => {
-                let translated_pl = self.translate_place(pl)?;
-                let translated_mt = TX::translate_mutability(*mt);
-
-                Ok(radium::Expr::AddressOf {
-                    mt: translated_mt,
-                    e: Box::new(translated_pl),
-                })
-            },
-
-            Rvalue::BinaryOp(op, operands) => {
-                let e1 = &operands.as_ref().0;
-                let e2 = &operands.as_ref().1;
-
-                let e1_ty = self.get_type_of_operand(e1);
-                let e2_ty = self.get_type_of_operand(e2);
-                let e1_st = self.ty_translator.translate_type_to_syn_type(e1_ty)?;
-                let e2_st = self.ty_translator.translate_type_to_syn_type(e2_ty)?;
-
-                let translated_e1 = self.translate_operand(e1, true)?;
-                let translated_e2 = self.translate_operand(e2, true)?;
-                let translated_op = self.translate_binop(*op, &operands.as_ref().0, &operands.as_ref().1)?;
-
-                Ok(radium::Expr::BinOp {
-                    o: translated_op,
-                    ot1: e1_st.into(),
-                    ot2: e2_st.into(),
-                    e1: Box::new(translated_e1),
-                    e2: Box::new(translated_e2),
-                })
-            },
-
-            Rvalue::CheckedBinaryOp(op, operands) => {
-                let e1 = &operands.as_ref().0;
-                let e2 = &operands.as_ref().1;
-
-                let e1_ty = self.get_type_of_operand(e1);
-                let e2_ty = self.get_type_of_operand(e2);
-                let e1_st = self.ty_translator.translate_type_to_syn_type(e1_ty)?;
-                let e2_st = self.ty_translator.translate_type_to_syn_type(e2_ty)?;
-
-                let translated_e1 = self.translate_operand(e1, true)?;
-                let translated_e2 = self.translate_operand(e2, true)?;
-                let translated_op = TX::translate_checked_binop(*op)?;
-
-                Ok(radium::Expr::BinOp {
-                    o: translated_op,
-                    ot1: e1_st.into(),
-                    ot2: e2_st.into(),
-                    e1: Box::new(translated_e1),
-                    e2: Box::new(translated_e2),
-                })
-            },
-
-            Rvalue::UnaryOp(op, operand) => {
-                let translated_e1 = self.translate_operand(operand, true)?;
-                let e1_ty = self.get_type_of_operand(operand);
-                let e1_st = self.ty_translator.translate_type_to_syn_type(e1_ty)?;
-                let translated_op = TX::translate_unop(*op, e1_ty)?;
-
-                Ok(radium::Expr::UnOp {
-                    o: translated_op,
-                    ot: e1_st.into(),
-                    e: Box::new(translated_e1),
-                })
-            },
-
-            Rvalue::NullaryOp(op, _ty) => {
-                // TODO: SizeOf
-                Err(TranslationError::UnsupportedFeature {
-                    description: "RefinedRust does currently not support nullary ops (AlignOf, Sizeof)"
-                        .to_owned(),
-                })
-            },
-
-            Rvalue::Discriminant(pl) => {
-                let ty = self.get_type_of_place(pl);
-                let translated_pl = self.translate_place(pl)?;
-                info!("getting discriminant of {:?} at type {:?}", pl, ty);
-
-                let ty::TyKind::Adt(adt_def, args) = ty.ty.kind() else {
-                    return Err(TranslationError::UnsupportedFeature {
-                        description: format!(
-                            "RefinedRust does currently not support discriminant accesses on non-enum types ({:?}, got {:?})",
-                            rval, ty.ty
-                        ),
-                    });
-                };
-
-                let enum_use = self.ty_translator.generate_enum_use(*adt_def, args.iter())?;
-                let els = enum_use.generate_raw_syn_type_term();
-
-                let discriminant_acc = radium::Expr::EnumDiscriminant {
-                    els: els.to_string(),
-                    e: Box::new(translated_pl),
-                };
-
-                // need to do a load from this place
-                let it = ty.ty.discriminant_ty(self.env.tcx());
-                let translated_it = self.ty_translator.translate_type(it)?;
-
-                let radium::Type::Int(translated_it) = translated_it else {
-                    return Err(TranslationError::UnknownError(format!(
-                        "type of discriminant is not an integer type {:?}",
-                        it
-                    )));
-                };
-
-                let ot = radium::OpType::Int(translated_it);
-
-                Ok(radium::Expr::Use {
-                    ot,
-                    e: Box::new(discriminant_acc),
-                })
-            },
-
-            Rvalue::Aggregate(kind, op) => {
-                // translate operands
-                let mut translated_ops: Vec<radium::Expr> = Vec::new();
-                let mut operand_types: Vec<Ty<'tcx>> = Vec::new();
-
-                for o in op {
-                    let translated_o = self.translate_operand(o, true)?;
-                    let type_of_o = self.get_type_of_operand(o);
-                    translated_ops.push(translated_o);
-                    operand_types.push(type_of_o);
-                }
-
-                match *kind {
-                    box mir::AggregateKind::Tuple => {
-                        if operand_types.is_empty() {
-                            // translate to unit literal
-                            return Ok(radium::Expr::Literal(radium::Literal::ZST));
-                        }
-
-                        let struct_use =
-                            self.ty_translator.generate_tuple_use(operand_types.iter().copied())?;
-                        let sl = struct_use.generate_raw_syn_type_term();
-                        let initializers: Vec<_> =
-                            translated_ops.into_iter().enumerate().map(|(i, o)| (i.to_string(), o)).collect();
-
-                        Ok(radium::Expr::StructInitE {
-                            sls: coq::term::App::new_lhs(sl.to_string()),
-                            components: initializers,
-                        })
-                    },
-
-                    box mir::AggregateKind::Adt(did, variant, args, ..) => {
-                        // get the adt def
-                        let adt_def: ty::AdtDef<'tcx> = self.env.tcx().adt_def(did);
-
-                        if adt_def.is_struct() {
-                            let variant = adt_def.variant(variant);
-                            let struct_use = self.ty_translator.generate_struct_use(variant.def_id, args)?;
-
-                            let Some(struct_use) = struct_use else {
-                                // if not, it's replaced by unit
-                                return Ok(radium::Expr::Literal(radium::Literal::ZST));
-                            };
-
-                            let sl = struct_use.generate_raw_syn_type_term();
-                            let initializers: Vec<_> = translated_ops
-                                .into_iter()
-                                .zip(variant.fields.iter())
-                                .map(|(o, field)| (field.name.to_string(), o))
-                                .collect();
-
-                            return Ok(radium::Expr::StructInitE {
-                                sls: coq::term::App::new_lhs(sl.to_string()),
-                                components: initializers,
-                            });
-                        }
-
-                        if adt_def.is_enum() {
-                            let variant_def = adt_def.variant(variant);
-
-                            let struct_use =
-                                self.ty_translator.generate_enum_variant_use(variant_def.def_id, args)?;
-                            let sl = struct_use.generate_raw_syn_type_term();
-
-                            let initializers: Vec<_> = translated_ops
-                                .into_iter()
-                                .zip(variant_def.fields.iter())
-                                .map(|(o, field)| (field.name.to_string(), o))
-                                .collect();
-
-                            let variant_e = radium::Expr::StructInitE {
-                                sls: coq::term::App::new_lhs(sl.to_string()),
-                                components: initializers,
-                            };
-
-                            let enum_use = self.ty_translator.generate_enum_use(adt_def, args)?;
-                            let els = enum_use.generate_raw_syn_type_term();
-
-                            info!("generating enum annotation for type {:?}", enum_use);
-                            let ty = radium::RustType::of_type(&radium::Type::Literal(enum_use));
-                            let variant_name = variant_def.name.to_string();
-
-                            return Ok(radium::Expr::EnumInitE {
-                                els: coq::term::App::new_lhs(els.to_string()),
-                                variant: variant_name,
-                                ty,
-                                initializer: Box::new(variant_e),
-                            });
-                        }
-
-                        // TODO
-                        Err(TranslationError::UnsupportedFeature {
-                            description: format!(
-                                "RefinedRust does currently not support aggregate rvalue for other ADTs (got: {:?})",
-                                rval
-                            ),
-                        })
-                    },
-                    box mir::AggregateKind::Closure(def, _args) => {
-                        trace!("Translating Closure aggregate value for {:?}", def);
-
-                        // We basically translate this to a tuple
-                        if operand_types.is_empty() {
-                            // translate to unit literal
-                            return Ok(radium::Expr::Literal(radium::Literal::ZST));
-                        }
-
-                        let struct_use =
-                            self.ty_translator.generate_tuple_use(operand_types.iter().copied())?;
-                        let sl = struct_use.generate_raw_syn_type_term();
-
-                        let initializers: Vec<_> =
-                            translated_ops.into_iter().enumerate().map(|(i, o)| (i.to_string(), o)).collect();
-
-                        Ok(radium::Expr::StructInitE {
-                            sls: coq::term::App::new_lhs(sl.to_string()),
-                            components: initializers,
-                        })
-                    },
-
-                    _ => {
-                        // TODO
-                        Err(TranslationError::UnsupportedFeature {
-                            description: format!(
-                                "RefinedRust does currently not support this kind of aggregate rvalue (got: {:?})",
-                                rval
-                            ),
-                        })
-                    },
-                }
-            },
-
-            Rvalue::Cast(kind, op, to_ty) => {
-                let op_ty = self.get_type_of_operand(op);
-                let op_st = self.ty_translator.translate_type_to_syn_type(op_ty)?;
-                let op_ot = op_st.into();
-
-                let translated_op = self.translate_operand(op, true)?;
-
-                let target_st = self.ty_translator.translate_type_to_syn_type(*to_ty)?;
-                let target_ot = target_st.into();
-
-                match kind {
-                    mir::CastKind::PointerCoercion(x) => {
-                        match x {
-                            ty::adjustment::PointerCoercion::MutToConstPointer => {
-                                // this is a NOP in our model
-                                Ok(translated_op)
-                            },
-
-                            ty::adjustment::PointerCoercion::ArrayToPointer
-                            | ty::adjustment::PointerCoercion::ClosureFnPointer(_)
-                            | ty::adjustment::PointerCoercion::ReifyFnPointer
-                            | ty::adjustment::PointerCoercion::UnsafeFnPointer
-                            | ty::adjustment::PointerCoercion::Unsize => {
-                                Err(TranslationError::UnsupportedFeature {
-                                    description: format!(
-                                        "RefinedRust does currently not support this kind of pointer coercion (got: {:?})",
-                                        rval
-                                    ),
-                                })
-                            },
-                        }
-                    },
-
-                    mir::CastKind::DynStar => Err(TranslationError::UnsupportedFeature {
-                        description: "RefinedRust does currently not support dyn* cast".to_owned(),
-                    }),
-
-                    mir::CastKind::IntToInt => {
-                        // Cast integer to integer
-                        Ok(radium::Expr::UnOp {
-                            o: radium::Unop::Cast(target_ot),
-                            ot: op_ot,
-                            e: Box::new(translated_op),
-                        })
-                    },
-
-                    mir::CastKind::IntToFloat => Err(TranslationError::UnsupportedFeature {
-                        description: "RefinedRust does currently not support int-to-float cast".to_owned(),
-                    }),
-
-                    mir::CastKind::FloatToInt => Err(TranslationError::UnsupportedFeature {
-                        description: "RefinedRust does currently not support float-to-int cast".to_owned(),
-                    }),
-
-                    mir::CastKind::FloatToFloat => Err(TranslationError::UnsupportedFeature {
-                        description: "RefinedRust does currently not support float-to-float cast".to_owned(),
-                    }),
-
-                    mir::CastKind::PtrToPtr => {
-                        match (op_ty.kind(), to_ty.kind()) {
-                            (TyKind::RawPtr(_), TyKind::RawPtr(_)) => {
-                                // Casts between raw pointers are NOPs for us
-                                Ok(translated_op)
-                            },
-
-                            _ => {
-                                // TODO: any other cases we should handle?
-                                Err(TranslationError::UnsupportedFeature {
-                                    description: format!(
-                                        "RefinedRust does currently not support ptr-to-ptr cast (got: {:?})",
-                                        rval
-                                    ),
-                                })
-                            },
-                        }
-                    },
-
-                    mir::CastKind::FnPtrToPtr => Err(TranslationError::UnsupportedFeature {
-                        description: format!(
-                            "RefinedRust does currently not support fnptr-to-ptr cast (got: {:?})",
-                            rval
-                        ),
-                    }),
-
-                    mir::CastKind::Transmute => Err(TranslationError::UnsupportedFeature {
-                        description: format!(
-                            "RefinedRust does currently not support transmute cast (got: {:?})",
-                            rval
-                        ),
-                    }),
-
-                    mir::CastKind::PointerExposeAddress => {
-                        // Cast pointer to integer
-                        Ok(radium::Expr::UnOp {
-                            o: radium::Unop::Cast(target_ot),
-                            ot: radium::OpType::Ptr,
-                            e: Box::new(translated_op),
-                        })
-                    },
-
-                    mir::CastKind::PointerFromExposedAddress => Err(TranslationError::UnsupportedFeature {
-                        description: format!(
-                            "RefinedRust does currently not support this kind of cast (got: {:?})",
-                            rval
-                        ),
-                    }),
-                }
-            },
-
-            Rvalue::CopyForDeref(_)
-            | Rvalue::Len(..)
-            | Rvalue::Repeat(..)
-            | Rvalue::ThreadLocalRef(..)
-            | Rvalue::ShallowInitBox(_, _) => Err(TranslationError::UnsupportedFeature {
-                description: format!(
-                    "RefinedRust does currently not support this kind of rvalue (got: {:?})",
-                    rval
-                ),
-            }),
-        }
-    }
-
-    /// Make a trivial place accessing `local`.
-    fn make_local_place(&self, local: Local) -> Place<'tcx> {
-        Place {
-            local,
-            projection: self.env.tcx().mk_place_elems(&[]),
-        }
-    }
-
-    /// Translate an operand.
-    /// This will either generate an lvalue (in case of Move or Copy) or an rvalue (in most cases
-    /// of Constant). How this is used depends on the context. (e.g., Use of an integer constant
-    /// does not typecheck, and produces a stuck program).
-    fn translate_operand(
-        &mut self,
-        op: &Operand<'tcx>,
-        to_rvalue: bool,
-    ) -> Result<radium::Expr, TranslationError<'tcx>> {
-        match op {
-            // In Caesium: typed_place needs deref (not use) for place accesses.
-            // use is used top-level to convert an lvalue to an rvalue, which is why we use it here.
-            Operand::Copy(place) | Operand::Move(place) => {
-                // check if this goes to a temporary of a checked op
-                let place_kind = if self.checked_op_temporaries.contains_key(&place.local) {
-                    assert!(place.projection.len() == 1);
-
-                    let ProjectionElem::Field(f, _0) = place.projection[0] else {
-                        unreachable!("invariant violation for access to checked op temporary");
-                    };
-
-                    if f.index() != 0 {
-                        // make this a constant false -- our semantics directly checks for overflows
-                        // and otherwise throws UB.
-                        return Ok(radium::Expr::Literal(radium::Literal::Bool(false)));
-                    }
-
-                    // access to the result of the op
-                    self.make_local_place(place.local)
-                } else {
-                    *place
-                };
-
-                let translated_place = self.translate_place(&place_kind)?;
-                let ty = self.get_type_of_place(place);
-
-                let st = self.ty_translator.translate_type_to_syn_type(ty.ty)?;
-
-                if to_rvalue {
-                    Ok(radium::Expr::Use {
-                        ot: st.into(),
-                        e: Box::new(translated_place),
-                    })
-                } else {
-                    Ok(translated_place)
-                }
-            },
-            Operand::Constant(constant) => {
-                // TODO: possibly need different handling of the rvalue flag
-                // when this also handles string literals etc.
-                return self.translate_constant(constant.as_ref());
-            },
-        }
-    }
-
-    /// Resolve the trait requirements of a function call.
-    /// The target of the call, [did], should have been resolved as much as possible,
-    /// as the requirements of a call can be different depending on which impl we consider.
-    fn resolve_trait_requirements_of_call(
-        &self,
-        did: DefId,
-        params: ty::GenericArgsRef<'tcx>,
-    ) -> Result<Vec<radium::TraitReqInst<'def, Ty<'tcx>>>, TranslationError<'tcx>> {
-        let mut scope = self.ty_translator.scope.borrow_mut();
-        let mut state = types::STInner::InFunction(&mut scope);
-        self.trait_registry.resolve_trait_requirements_in_state(&mut state, did, params)
-    }
-
-    /// Translate the use of an `FnDef`, registering that the current function needs to link against
-    /// a particular monomorphization of the used function.
-    /// Is guaranteed to return a `radium::Expr::CallTarget` with the parameter instantiation of
-    /// this function annotated.
-    fn translate_fn_def_use(&mut self, ty: Ty<'tcx>) -> Result<radium::Expr, TranslationError<'tcx>> {
-        let TyKind::FnDef(defid, params) = ty.kind() else {
-            return Err(TranslationError::UnknownError("not a FnDef type".to_owned()));
-        };
-
-        let current_param_env: ty::ParamEnv<'tcx> = self.env.tcx().param_env(self.proc.get_id());
-
-        // Check whether we are calling into a trait method.
-        // This works since we did not resolve concrete instances, so this is always an abstract
-        // reference to the trait.
-        let calling_trait = self.env.tcx().trait_of_item(*defid);
-
-        // Check whether we are calling a plain function or a trait method
-        let Some(calling_trait) = calling_trait else {
-            // resolve the trait requirements
-            let trait_spec_terms = self.resolve_trait_requirements_of_call(*defid, params)?;
-
-            // track that we are using this function and generate the Coq location name
-            let (code_param_name, ty_hint, lft_hint) =
-                self.register_use_procedure(*defid, vec![], params, trait_spec_terms)?;
-
-            let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
-
-            return Ok(radium::Expr::CallTarget(code_param_name, ty_hint, lft_hint));
-        };
-
-        // Otherwise, we are calling a trait method
-        // Resolve the trait instance using trait selection
-        let Some((resolved_did, resolved_params, kind)) =
-            resolution::resolve_assoc_item(self.env.tcx(), current_param_env, *defid, params)
-        else {
-            return Err(TranslationError::TraitResolution(format!("Could not resolve trait {:?}", defid)));
-        };
-
-        info!(
-            "Resolved trait method {:?} as {:?} with substs {:?} and kind {:?}",
-            defid, resolved_did, resolved_params, kind
-        );
-
-        match kind {
-            resolution::TraitResolutionKind::UserDefined => {
-                // We can statically resolve the particular trait instance,
-                // but need to apply the spec to the instance's spec attributes
-
-                // resolve the trait requirements
-                let trait_spec_terms =
-                    self.resolve_trait_requirements_of_call(resolved_did, resolved_params)?;
-
-                let (param_name, ty_hint, lft_hint) =
-                    self.register_use_procedure(resolved_did, vec![], resolved_params, trait_spec_terms)?;
-                let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
-
-                Ok(radium::Expr::CallTarget(param_name, ty_hint, lft_hint))
-            },
-
-            resolution::TraitResolutionKind::Param => {
-                // In this case, we have already applied it to the spec attribute
-
-                // resolve the trait requirements
-                let trait_spec_terms = self.resolve_trait_requirements_of_call(*defid, params)?;
-
-                let (param_name, ty_hint, lft_hint) =
-                    self.register_use_trait_method(resolved_did, resolved_params, trait_spec_terms)?;
-                let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
-
-                Ok(radium::Expr::CallTarget(param_name, ty_hint, lft_hint))
-            },
-
-            resolution::TraitResolutionKind::Closure => {
-                // TODO: here, we should first generate an instance of the trait
-                //let body = self.env.tcx().instance_mir(middle::ty::InstanceDef::Item(resolved_did));
-                //let body = self.env.tcx().instance_mir(middle::ty::InstanceDef::FnPtrShim(*defid, ty));
-                //info!("closure body: {:?}", body);
-
-                //FunctionTranslator::dump_body(body);
-
-                //let res_result = ty::Instance::resolve(self.env.tcx(), callee_param_env, *defid, params);
-                //info!("Resolution {:?}", res_result);
-
-                // the args are just the closure args. We can ignore them.
-                let _clos_args = resolved_params.as_closure();
-
-                // resolve the trait requirements
-                let trait_spec_terms = self.resolve_trait_requirements_of_call(*defid, params)?;
-
-                let (param_name, ty_hint, lft_hint) =
-                    self.register_use_procedure(resolved_did, vec![], ty::List::empty(), trait_spec_terms)?;
-                let ty_hint = ty_hint.into_iter().map(|x| radium::RustType::of_type(&x)).collect();
-
-                Ok(radium::Expr::CallTarget(param_name, ty_hint, lft_hint))
-            },
-        }
-    }
-
-    /// Translate a scalar at a specific type to a `radium::Expr`.
-    // TODO: Use `TryFrom` instead
-    fn translate_scalar(
-        &mut self,
-        sc: &Scalar,
-        ty: Ty<'tcx>,
-    ) -> Result<radium::Expr, TranslationError<'tcx>> {
-        // TODO: Use `TryFrom` instead
-        fn translate_literal<'tcx, T, U>(
-            sc: Result<T, U>,
-            fptr: fn(T) -> radium::Literal,
-        ) -> Result<radium::Expr, TranslationError<'tcx>> {
-            sc.map_or(Err(TranslationError::InvalidLayout), |lit| Ok(radium::Expr::Literal(fptr(lit))))
-        }
-
-        match ty.kind() {
-            TyKind::Bool => translate_literal(sc.to_bool(), radium::Literal::Bool),
-
-            TyKind::Int(it) => match it {
-                ty::IntTy::I8 => translate_literal(sc.to_i8(), radium::Literal::I8),
-                ty::IntTy::I16 => translate_literal(sc.to_i16(), radium::Literal::I16),
-                ty::IntTy::I32 => translate_literal(sc.to_i32(), radium::Literal::I32),
-                ty::IntTy::I128 => translate_literal(sc.to_i128(), radium::Literal::I128),
-
-                // For Radium, the pointer size is 8 bytes
-                ty::IntTy::I64 | ty::IntTy::Isize => translate_literal(sc.to_i64(), radium::Literal::I64),
-            },
-
-            TyKind::Uint(it) => match it {
-                ty::UintTy::U8 => translate_literal(sc.to_u8(), radium::Literal::U8),
-                ty::UintTy::U16 => translate_literal(sc.to_u16(), radium::Literal::U16),
-                ty::UintTy::U32 => translate_literal(sc.to_u32(), radium::Literal::U32),
-                ty::UintTy::U128 => translate_literal(sc.to_u128(), radium::Literal::U128),
-
-                // For Radium, the pointer is 8 bytes
-                ty::UintTy::U64 | ty::UintTy::Usize => translate_literal(sc.to_u64(), radium::Literal::U64),
-            },
-
-            TyKind::Char => translate_literal(sc.to_char(), radium::Literal::Char),
-
-            TyKind::FnDef(_, _) => self.translate_fn_def_use(ty),
-
-            TyKind::Tuple(tys) => {
-                if tys.is_empty() {
-                    return Ok(radium::Expr::Literal(radium::Literal::ZST));
-                }
-
-                Err(TranslationError::UnsupportedFeature {
-                    description: format!(
-                        "RefinedRust does currently not support compound construction of tuples using literals (got: {:?})",
-                        ty
-                    ),
-                })
-            },
-
-            TyKind::Ref(_, _, _) => match sc {
-                Scalar::Int(_) => unreachable!(),
-
-                Scalar::Ptr(pointer, _) => {
-                    let glob_alloc = self.env.tcx().global_alloc(pointer.provenance);
-                    match glob_alloc {
-                        middle::mir::interpret::GlobalAlloc::Static(did) => {
-                            info!(
-                                "Found static GlobalAlloc {:?} for Ref scalar {:?} at type {:?}",
-                                did, sc, ty
-                            );
-
-                            let s = self.const_registry.get_static(did)?;
-                            self.collected_statics.insert(did);
-                            Ok(radium::Expr::Literal(radium::Literal::Loc(s.loc_name.clone())))
-                        },
-                        middle::mir::interpret::GlobalAlloc::Memory(alloc) => {
-                            // TODO: this is needed
-                            Err(TranslationError::UnsupportedFeature {
-                                description: format!(
-                                    "RefinedRust does currently not support GlobalAlloc {:?} for scalar {:?} at type {:?}",
-                                    glob_alloc, sc, ty
-                                ),
-                            })
-                        },
-                        _ => Err(TranslationError::UnsupportedFeature {
-                            description: format!(
-                                "RefinedRust does currently not support GlobalAlloc {:?} for scalar {:?} at type {:?}",
-                                glob_alloc, sc, ty
-                            ),
-                        }),
-                    }
-                },
-            },
-
-            _ => Err(TranslationError::UnsupportedFeature {
-                description: format!(
-                    "RefinedRust does currently not support layout for const value: (got: {:?})",
-                    ty
-                ),
-            }),
-        }
-    }
-
-    /// Translate a constant value from const evaluation.
-    fn translate_constant_value(
-        &mut self,
-        v: mir::interpret::ConstValue<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Result<radium::Expr, TranslationError<'tcx>> {
-        match v {
-            ConstValue::Scalar(sc) => self.translate_scalar(&sc, ty),
-            ConstValue::ZeroSized => {
-                // TODO are there more special cases we need to handle somehow?
-                match ty.kind() {
-                    TyKind::FnDef(_, _) => {
-                        info!("Translating ZST val for function call target: {:?}", ty);
-                        self.translate_fn_def_use(ty)
-                    },
-                    _ => Ok(radium::Expr::Literal(radium::Literal::ZST)),
-                }
-            },
-            _ => {
-                // TODO: do we actually care about this case or is this just something that can
-                // appear as part of CTFE/MIRI?
-                Err(TranslationError::UnsupportedFeature {
-                    description: format!("Unsupported Constant: ConstValue; {:?}", v),
-                })
-            },
-        }
-    }
-
-    /// Translate a Constant to a `radium::Expr`.
-    fn translate_constant(
-        &mut self,
-        constant: &Constant<'tcx>,
-    ) -> Result<radium::Expr, TranslationError<'tcx>> {
-        match constant.literal {
-            ConstantKind::Ty(v) => {
-                let const_ty = v.ty();
-
-                match v.kind() {
-                    ConstKind::Value(v) => {
-                        // this doesn't contain the necessary structure anymore. Need to reconstruct using the
-                        // type.
-                        match v.try_to_scalar() {
-                            Some(sc) => self.translate_scalar(&sc, const_ty),
-                            _ => Err(TranslationError::UnsupportedFeature {
-                                description: format!("const value not supported: {:?}", v),
-                            }),
-                        }
-                    },
-                    _ => Err(TranslationError::UnsupportedFeature {
-                        description: "Unsupported ConstKind".to_owned(),
-                    }),
-                }
-            },
-            ConstantKind::Val(val, ty) => self.translate_constant_value(val, ty),
-            ConstantKind::Unevaluated(c, ty) => {
-                // call const evaluation
-                let param_env: ty::ParamEnv<'tcx> = self.env.tcx().param_env(self.proc.get_id());
-                match self.env.tcx().const_eval_resolve(param_env, c, None) {
-                    Ok(res) => self.translate_constant_value(res, ty),
-                    Err(e) => match e {
-                        ErrorHandled::Reported(_) => Err(TranslationError::UnsupportedFeature {
-                            description: "Cannot interpret constant".to_owned(),
-                        }),
-                        ErrorHandled::TooGeneric => Err(TranslationError::UnsupportedFeature {
-                            description: "Const use is too generic".to_owned(),
-                        }),
-                    },
-                }
-            },
-        }
-    }
-
-    /// Translate a place to a Caesium lvalue.
-    fn translate_place(&mut self, pl: &Place<'tcx>) -> Result<radium::Expr, TranslationError<'tcx>> {
-        // Get the type of the underlying local. We will use this to
-        // get the necessary layout information for dereferencing
-        let mut cur_ty = self.get_type_of_local(pl.local).map(PlaceTy::from_ty)?;
-
-        let local_name = self
-            .variable_map
-            .get(&pl.local)
-            .ok_or_else(|| TranslationError::UnknownVar(format!("{:?}", pl.local)))?;
-
-        let mut acc_expr = radium::Expr::Var(local_name.to_string());
-
-        // iterate in evaluation order
-        for it in pl.projection {
-            match &it {
-                ProjectionElem::Deref => {
-                    // use the type of the dereferencee
-                    let st = self.ty_translator.translate_type_to_syn_type(cur_ty.ty)?;
-                    acc_expr = radium::Expr::Deref {
-                        ot: st.into(),
-                        e: Box::new(acc_expr),
-                    };
-                },
-                ProjectionElem::Field(f, _) => {
-                    // `t` is the type of the field we are accessing!
-                    let lit = self.ty_translator.generate_structlike_use(cur_ty.ty, cur_ty.variant_index)?;
-                    // TODO: does not do the right thing for accesses to fields of zero-sized objects.
-                    let struct_sls = lit.map_or(radium::SynType::Unit, |x| x.generate_raw_syn_type_term());
-                    let name = self.ty_translator.translator.get_field_name_of(
-                        *f,
-                        cur_ty.ty,
-                        cur_ty.variant_index.map(abi::VariantIdx::as_usize),
-                    )?;
-
-                    acc_expr = radium::Expr::FieldOf {
-                        e: Box::new(acc_expr),
-                        name,
-                        sls: struct_sls.to_string(),
-                    };
-                },
-                ProjectionElem::Index(_v) => {
-                    //TODO
-                    return Err(TranslationError::UnsupportedFeature {
-                        description: "places: implement index access".to_owned(),
-                    });
-                },
-                ProjectionElem::ConstantIndex { .. } => {
-                    //TODO
-                    return Err(TranslationError::UnsupportedFeature {
-                        description: "places: implement const index access".to_owned(),
-                    });
-                },
-                ProjectionElem::Subslice { .. } => {
-                    return Err(TranslationError::UnsupportedFeature {
-                        description: "places: implement subslicing".to_owned(),
-                    });
-                },
-                ProjectionElem::Downcast(_, variant_idx) => {
-                    info!("Downcast of ty {:?} to {:?}", cur_ty, variant_idx);
-                    if let ty::TyKind::Adt(def, args) = cur_ty.ty.kind() {
-                        if def.is_enum() {
-                            let enum_use = self.ty_translator.generate_enum_use(*def, args.iter())?;
-                            let els = enum_use.generate_raw_syn_type_term();
-
-                            let variant_name = types::TX::get_variant_name_of(cur_ty.ty, *variant_idx)?;
-
-                            acc_expr = radium::Expr::EnumData {
-                                els: els.to_string(),
-                                variant: variant_name,
-                                e: Box::new(acc_expr),
-                            }
-                        } else {
-                            return Err(TranslationError::UnknownError(
-                                "places: ADT downcasting on non-enum type".to_owned(),
-                            ));
-                        }
-                    } else {
-                        return Err(TranslationError::UnknownError(
-                            "places: ADT downcasting on non-enum type".to_owned(),
-                        ));
-                    }
-                },
-                ProjectionElem::OpaqueCast(_) => {
-                    return Err(TranslationError::UnsupportedFeature {
-                        description: "places: implement opaque casts".to_owned(),
-                    });
-                },
-            };
-            // update cur_ty
-            cur_ty = cur_ty.projection_ty(self.env.tcx(), it);
-        }
-        info!("translating place {:?} to {:?}", pl, acc_expr);
-        Ok(acc_expr)
-    }
-
-    /// Get the type of a local in a body.
-    fn get_type_of_local(&self, local: Local) -> Result<Ty<'tcx>, TranslationError<'tcx>> {
-        self.proc
-            .get_mir()
-            .local_decls
-            .get(local)
-            .map(|decl| decl.ty)
-            .ok_or_else(|| TranslationError::UnknownVar(String::new()))
-    }
-
-    /// Get the type of a place expression.
-    fn get_type_of_place(&self, pl: &Place<'tcx>) -> PlaceTy<'tcx> {
-        pl.ty(&self.proc.get_mir().local_decls, self.env.tcx())
-    }
-
-    /// Get the type of a const.
-    fn get_type_of_const(cst: &Constant<'tcx>) -> Ty<'tcx> {
-        match cst.literal {
-            ConstantKind::Ty(cst) => cst.ty(),
-            ConstantKind::Val(_, ty) | ConstantKind::Unevaluated(_, ty) => ty,
-        }
-    }
-
-    /// Get the type of an operand.
-    fn get_type_of_operand(&self, op: &Operand<'tcx>) -> Ty<'tcx> {
-        op.ty(&self.proc.get_mir().local_decls, self.env.tcx())
-    }
-}
diff --git a/rr_frontend/translation/src/lib.rs b/rr_frontend/translation/src/lib.rs
index 2ab43fcf7451fd268e46817b37cfdbde8cf68149..b704a46a28b7796eb81c62c272492cd06feb37b6 100644
--- a/rr_frontend/translation/src/lib.rs
+++ b/rr_frontend/translation/src/lib.rs
@@ -8,6 +8,7 @@
 #![feature(box_patterns)]
 #![feature(let_chains)]
 #![feature(rustc_private)]
+
 mod attrs;
 mod base;
 mod body;
diff --git a/rr_frontend/translation/src/regions/assignment.rs b/rr_frontend/translation/src/regions/assignment.rs
index 8676d47c17299dfc776182969971e605061fe232..f7583c34e2f424939256ff6e4780f4d935d20bb2 100644
--- a/rr_frontend/translation/src/regions/assignment.rs
+++ b/rr_frontend/translation/src/regions/assignment.rs
@@ -6,24 +6,19 @@
 
 //! Provides functionality for generating lifetime annotations for assignments.
 
-use std::collections::{BTreeMap, HashMap, HashSet};
+use std::collections::HashSet;
 
-use derive_more::{Constructor, Debug};
 use log::{info, warn};
-use rr_rustc_interface::hir::def_id::DefId;
 use rr_rustc_interface::middle::mir::tcx::PlaceTy;
-use rr_rustc_interface::middle::mir::{BasicBlock, BorrowKind, Location, Rvalue};
+use rr_rustc_interface::middle::mir::{BorrowKind, Location, Rvalue};
 use rr_rustc_interface::middle::ty;
-use rr_rustc_interface::middle::ty::{Ty, TyCtxt, TyKind, TypeFoldable};
-use ty::TypeSuperFoldable;
+use rr_rustc_interface::middle::ty::{Ty, TypeFoldable};
 
 use crate::base::{self, Region};
 use crate::environment::borrowck::facts;
 use crate::environment::polonius_info::PoloniusInfo;
-use crate::environment::{dump_borrowck_info, polonius_info, Environment};
-use crate::regions::arg_folder::ty_instantiate;
+use crate::environment::Environment;
 use crate::regions::inclusion_tracker::{self, InclusionTracker};
-use crate::regions::EarlyLateRegionMap;
 use crate::{regions, types};
 
 /// Generate inclusions for a strong update assignment.
diff --git a/rr_frontend/translation/src/regions/calls.rs b/rr_frontend/translation/src/regions/calls.rs
index c0fb2db5321c0a8738d7174ab3d30b5da8ce2267..8e3b93148993d1a3ad22d1f324b4035d31faf907 100644
--- a/rr_frontend/translation/src/regions/calls.rs
+++ b/rr_frontend/translation/src/regions/calls.rs
@@ -6,40 +6,35 @@
 
 //! Provides functionality for generating lifetime annotations for calls.
 
-use std::collections::{BTreeMap, HashMap, HashSet};
+use std::collections::{HashMap, HashSet};
 
-use derive_more::{Constructor, Debug};
+use derive_more::Debug;
 use log::{info, warn};
-use rr_rustc_interface::hir::def_id::DefId;
-use rr_rustc_interface::middle::mir::tcx::PlaceTy;
-use rr_rustc_interface::middle::mir::{BasicBlock, BorrowKind, Location, Rvalue};
+use rr_rustc_interface::middle::mir::Location;
 use rr_rustc_interface::middle::ty;
-use rr_rustc_interface::middle::ty::{Ty, TyCtxt, TyKind, TypeFoldable, TypeFolder};
-use ty::TypeSuperFoldable;
+use rr_rustc_interface::middle::ty::TypeFolder;
 
-use crate::base::{self, Region};
 use crate::environment::borrowck::facts;
 use crate::environment::polonius_info::PoloniusInfo;
-use crate::environment::{dump_borrowck_info, polonius_info, Environment};
-use crate::regions::arg_folder::ty_instantiate;
-use crate::regions::inclusion_tracker::{self, InclusionTracker};
+use crate::environment::{polonius_info, Environment};
+use crate::regions::inclusion_tracker::InclusionTracker;
 use crate::regions::EarlyLateRegionMap;
-use crate::{regions, types};
+use crate::{base, regions, types};
 
 // solve the constraints for the new_regions
 // we first identify what kinds of constraints these new regions are subject to
 #[derive(Debug)]
 pub enum CallRegionKind {
     // this is just an intersection of local regions.
-    Intersection(HashSet<Region>),
+    Intersection(HashSet<base::Region>),
     // this is equal to a specific region
-    EqR(Region),
+    EqR(base::Region),
 }
 
 pub struct CallRegions {
-    pub early_regions: Vec<Region>,
-    pub late_regions: Vec<Region>,
-    pub classification: HashMap<Region, CallRegionKind>,
+    pub early_regions: Vec<base::Region>,
+    pub late_regions: Vec<base::Region>,
+    pub classification: HashMap<base::Region, CallRegionKind>,
 }
 
 // `substs` are the substitutions for the early-bound regions
@@ -108,7 +103,7 @@ pub fn compute_call_regions<'tcx>(
         }
     }
     // first sort this to enable cycle resolution
-    let mut new_regions_sorted: Vec<Region> = new_regions.iter().copied().collect();
+    let mut new_regions_sorted: Vec<base::Region> = new_regions.iter().copied().collect();
     new_regions_sorted.sort();
 
     // identify the late-bound regions
diff --git a/rr_frontend/translation/src/regions/composite.rs b/rr_frontend/translation/src/regions/composite.rs
index f473b8ab42b449393fabef293832f720bb2084c7..2865cc38f28adde37f50d1fedbe1e5643bf12304 100644
--- a/rr_frontend/translation/src/regions/composite.rs
+++ b/rr_frontend/translation/src/regions/composite.rs
@@ -6,25 +6,17 @@
 
 //! Provides functionality for generating lifetime annotations for composite expressions.
 
-use std::collections::{BTreeMap, HashMap, HashSet};
+use std::collections::HashSet;
 
-use derive_more::{Constructor, Debug};
 use log::{info, warn};
-use rr_rustc_interface::hir::def_id::DefId;
-use rr_rustc_interface::middle::mir::tcx::PlaceTy;
-use rr_rustc_interface::middle::mir::{BasicBlock, BorrowKind, Location, Rvalue};
+use rr_rustc_interface::middle::mir::Location;
 use rr_rustc_interface::middle::ty;
-use rr_rustc_interface::middle::ty::{Ty, TyCtxt, TyKind, TypeFoldable, TypeFolder};
-use ty::TypeSuperFoldable;
+use rr_rustc_interface::middle::ty::{Ty, TypeFolder};
 
-use crate::base::{self, Region};
 use crate::environment::borrowck::facts;
-use crate::environment::polonius_info::PoloniusInfo;
-use crate::environment::{dump_borrowck_info, polonius_info, Environment};
-use crate::regions::arg_folder::ty_instantiate;
-use crate::regions::inclusion_tracker::{self, InclusionTracker};
-use crate::regions::EarlyLateRegionMap;
-use crate::{regions, types};
+use crate::environment::Environment;
+use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::types;
 
 /// On creating a composite value (e.g. a struct or enum), the composite value gets its own
 /// Polonius regions We need to map these regions properly to the respective lifetimes.
diff --git a/rr_frontend/translation/src/regions/init.rs b/rr_frontend/translation/src/regions/init.rs
index d329ff2b999801199f4c623f3e72dea4cfa044dc..571131f5274dc5d3b4da3695aeb6adc9edaa1e87 100644
--- a/rr_frontend/translation/src/regions/init.rs
+++ b/rr_frontend/translation/src/regions/init.rs
@@ -6,21 +6,18 @@
 
 //! Provides functionality for generating initial lifetime constraints.
 
-use std::collections::{BTreeMap, HashMap, HashSet};
+use std::collections::{BTreeMap, HashMap};
 
-use derive_more::{Constructor, Debug};
 use log::{info, warn};
 use rr_rustc_interface::hir::def_id::DefId;
-use rr_rustc_interface::middle::mir::tcx::PlaceTy;
 use rr_rustc_interface::middle::mir::{BasicBlock, Location};
 use rr_rustc_interface::middle::ty;
-use rr_rustc_interface::middle::ty::{Ty, TyCtxt, TyKind, TypeFoldable};
-use ty::TypeSuperFoldable;
+use rr_rustc_interface::middle::ty::Ty;
 
-use crate::base::{self, Region};
+use crate::base;
 use crate::environment::borrowck::facts;
 use crate::environment::polonius_info::PoloniusInfo;
-use crate::environment::{dump_borrowck_info, polonius_info, Environment};
+use crate::environment::{polonius_info, Environment};
 use crate::regions::arg_folder::ty_instantiate;
 use crate::regions::inclusion_tracker::InclusionTracker;
 use crate::regions::EarlyLateRegionMap;
diff --git a/rr_frontend/translation/src/regions/mod.rs b/rr_frontend/translation/src/regions/mod.rs
index efbc908d42b6039b9560668d7ae02d73a20c294c..3a6039d326cc15413ec3307a569ebfcbe58851bf 100644
--- a/rr_frontend/translation/src/regions/mod.rs
+++ b/rr_frontend/translation/src/regions/mod.rs
@@ -15,21 +15,15 @@ pub mod init;
 
 use std::collections::{BTreeMap, HashMap, HashSet};
 
-use arg_folder::ty_instantiate;
 use derive_more::{Constructor, Debug};
 use log::{info, warn};
-use rr_rustc_interface::hir::def_id::DefId;
-use rr_rustc_interface::middle::mir::tcx::PlaceTy;
-use rr_rustc_interface::middle::mir::{BasicBlock, Location};
 use rr_rustc_interface::middle::ty;
-use rr_rustc_interface::middle::ty::{Ty, TyCtxt, TyKind, TypeFoldable};
+use rr_rustc_interface::middle::ty::TyCtxt;
 use ty::TypeSuperFoldable;
 
-use crate::base::{self, Region};
+use crate::base::Region;
 use crate::environment::borrowck::facts;
-use crate::environment::polonius_info::PoloniusInfo;
-use crate::environment::{dump_borrowck_info, polonius_info, Environment};
-use crate::regions::inclusion_tracker::InclusionTracker;
+use crate::environment::polonius_info;
 
 /// Collect all the regions appearing in a type.
 /// Data structure that maps early and late region indices inside functions to Polonius regions.