charon_lib/transform/update_closure_signatures.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
//! # Micro-pass: the first local variable of closures is (a borrow to) the closure itself. This is
//! not consistent with the closure signature, which represents the captured state as a tuple. This
//! micro-pass updates this.
use crate::ids::Vector;
use crate::transform::TransformCtx;
use crate::ullbc_ast::*;
use derive_generic_visitor::{Continue, ControlFlow, Visitor};
use super::ctx::UllbcPass;
#[derive(Visitor)]
struct InsertRegions<'a> {
regions: &'a mut Vector<RegionId, RegionVar>,
// The number of region groups we dived into (we don't count the regions
// at the declaration level). We use this for the DeBruijn indices.
depth: usize,
}
impl VisitAstMut for InsertRegions<'_> {
fn exit_region(&mut self, r: &mut Region) {
if r == &Region::Erased {
// Insert a fresh region
let index = self
.regions
.push_with(|index| RegionVar { index, name: None });
*r = Region::Var(DeBruijnVar::bound(DeBruijnId::new(self.depth), index));
}
}
fn visit_region_binder<T: AstVisitable>(
&mut self,
x: &mut RegionBinder<T>,
) -> ControlFlow<Self::Break> {
self.depth += 1;
self.visit_inner(x)?;
self.depth -= 1;
Continue(())
}
}
fn transform_function(_ctx: &TransformCtx, def: &mut FunDecl) -> Result<(), Error> {
let FunSig {
closure_info,
inputs,
generics,
..
} = &mut def.signature;
if let Some(info) = closure_info {
// Explore the state and introduce fresh regions for the erased regions we find.
let mut visitor = InsertRegions {
regions: &mut generics.regions,
depth: 0,
};
inputs[0].drive_mut(&mut visitor);
// Update the body.
// We change the type of the local variable of index 1, which is a reference to the closure
// itself, so that it has the type of (a reference to) the state of the closure.
//
// For instance, in the example below:
// ```text
// pub fn test_closure_capture(x: u32, y: u32) -> u32 {
// let f = &|z| x + y + z;
// (f)(0)
// }
// ```
//
// The first local variable in the body of the closure has type:
// `&'_ (fn(u32) -> u32)`
// We change it to:
// ```
// &'_ (&u32, &u32)
// ```
// We also update the corresponding field accesses in the body of
// the function.
if let Ok(body) = &mut def.body {
let body = body.as_unstructured_mut().unwrap();
// Update the type of the local 1 (which is the closure)
assert!(body.locals.vars.len() > 1);
let state_var = &mut body.locals.vars[1];
state_var.ty = inputs[0].clone();
state_var.name = Some("state".to_string());
// Update the body, and in particular the accesses to the states
// TODO: translate to ADT field access
// TODO: handle directly during translation
let num_fields = info.state.len();
body.body.dyn_visit_in_body_mut(|pe: &mut ProjectionElem| {
if let ProjectionElem::Field(pk @ FieldProjKind::ClosureState, _) = pe {
*pk = FieldProjKind::Tuple(num_fields);
}
});
}
Ok(())
} else {
Ok(())
}
}
pub struct Transform;
impl UllbcPass for Transform {
fn transform_function(&self, ctx: &mut TransformCtx, def: &mut FunDecl) {
// Ignore the errors, which should have been reported
let _ = transform_function(ctx, def);
}
}