1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

adapt inliner, remove String from function name, add stack to typed identifier

This commit is contained in:
schaeff 2019-06-06 19:06:16 +02:00
parent 2a7b33f8c1
commit addf79f20e
7 changed files with 327 additions and 311 deletions

View file

@ -47,7 +47,7 @@ impl<'ast> Flattener<'ast> {
// Load IfElse helper
let ie = TypedFunction {
id: "_if_else_field".to_string(),
id: "_if_else_field",
arguments: vec![
Parameter {
id: Variable {
@ -1230,7 +1230,7 @@ impl<'ast> Flattener<'ast> {
}
FlatFunction {
id: funct.id.clone(),
id: funct.id.to_string(),
arguments: arguments_flattened,
statements: statements_flattened,
signature: funct.signature,
@ -1272,7 +1272,7 @@ impl<'ast> Flattener<'ast> {
Type::FieldElementArray(size) => self.issue_new_variables(size),
};
self.layout.insert(variable.id, vars.clone());
self.layout.insert(variable.id.clone(), vars.clone());
vars
}
@ -1398,7 +1398,7 @@ mod tests {
);
let fun = TypedFunction {
id: String::from("main"),
id: "main",
arguments: vec![],
statements: vec![statement],
signature: Signature {
@ -1473,7 +1473,7 @@ mod tests {
let mut functions_flattened = vec![];
let funct = TypedFunction {
id: "foo".to_string(),
id: "foo",
signature: Signature::new()
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement]),
@ -1523,7 +1523,7 @@ mod tests {
// return foo()
let foo = TypedFunction {
id: String::from("foo"),
id: "foo",
arguments: vec![],
statements: vec![
TypedStatement::Definition(
@ -1539,7 +1539,7 @@ mod tests {
};
let main = TypedFunction {
id: String::from("main"),
id: "main",
arguments: vec![],
statements: vec![TypedStatement::Return(vec![
FieldElementExpression::FunctionCall(String::from("foo"), vec![]).into(),
@ -1589,7 +1589,7 @@ mod tests {
// return _3
let function = TypedFunction {
id: String::from("main"),
id: "main",
arguments: vec![],
statements: vec![
TypedStatement::Definition(
@ -1672,7 +1672,7 @@ mod tests {
let mut flattener = Flattener::new();
let functions = vec![
TypedFunction {
id: "foo".to_string(),
id: "foo",
arguments: vec![],
statements: vec![TypedStatement::Return(vec![TypedExpression::FieldElement(
FieldElementExpression::Number(FieldPrime::from(1)),
@ -1682,7 +1682,7 @@ mod tests {
.outputs(vec![Type::FieldElement]),
},
TypedFunction {
id: "foo".to_string(),
id: "foo",
arguments: vec![],
statements: vec![TypedStatement::Return(vec![
TypedExpression::FieldElement(FieldElementExpression::Number(
@ -1697,7 +1697,7 @@ mod tests {
.outputs(vec![Type::FieldElement, Type::FieldElement]),
},
TypedFunction {
id: "main".to_string(),
id: "main",
arguments: vec![],
statements: vec![
TypedStatement::Definition(

View file

@ -279,7 +279,7 @@ impl<'ast> Checker<'ast> {
}
Ok(TypedFunction {
id: funct.id.to_string(),
id: funct.id,
arguments: funct
.arguments
.iter()

View file

@ -1,326 +1,322 @@
// use crate::typed_absy::folder::*;
// use crate::typed_absy::Folder;
// use crate::typed_absy::*;
// use crate::types::{Signature, Type};
// use std::collections::HashMap;
// use zokrates_field::field::Field;
use crate::typed_absy::folder::*;
use crate::typed_absy::Folder;
use crate::typed_absy::*;
use crate::types::{Signature, Type};
use std::collections::HashMap;
use zokrates_field::field::Field;
// pub struct Inliner<'ast, T: Field> {
// functions: Vec<TypedFunction<'ast, T>>,
// statements_buffer: Vec<TypedStatement<'ast, T>>,
// context: Vec<(String, usize)>,
// call_count: HashMap<String, usize>,
// }
pub struct Inliner<'ast, T: Field> {
functions: Vec<TypedFunction<'ast, T>>,
statements_buffer: Vec<TypedStatement<'ast, T>>,
context: Vec<(&'ast str, Signature, usize)>,
call_count: HashMap<String, usize>,
}
// impl<'ast, T: Field> Inliner<'ast, T> {
// pub fn new() -> Self {
// Inliner {
// functions: vec![],
// statements_buffer: vec![],
// context: vec![],
// call_count: HashMap::new(),
// }
// }
impl<'ast, T: Field> Inliner<'ast, T> {
pub fn new() -> Self {
Inliner {
functions: vec![],
statements_buffer: vec![],
context: vec![],
call_count: HashMap::new(),
}
}
// fn should_inline(
// &self,
// function: &Option<TypedFunction<T>>,
// arguments: &Vec<TypedExpression<T>>,
// ) -> bool {
// // we should define a heuristic here
// // currently it doesn't seem like there's a tradeoff as everything gets inlined in flattening anyway (apart from compiling performance, as inlining
// // in flattening should be faster and less memory intensive)
// // however, using backends such as bellman, we could avoid flattening and "stream" the computation
// // at proving time, the tradeoff becomes code size (not inlining keeps only one copy of each function) vs optimisation
// // (inlining enables constant propagation through function calls, which cannot be achieved by our final optimiser in some cases)
// // for now, we inline functions whose non-array parameters are constant, as this covers our main use case for inlining: propagation of
// // constant array indices
// match function {
// Some(..) => {
// // check whether non-array arguments are constant
// arguments.iter().all(|e| match e {
// TypedExpression::FieldElementArray(..) => true,
// TypedExpression::FieldElement(FieldElementExpression::Number(..)) => true,
// TypedExpression::Boolean(BooleanExpression::Value(..)) => true,
// _ => false,
// })
// }
// None => false,
// }
// }
fn should_inline(
&self,
function: &Option<TypedFunction<T>>,
arguments: &Vec<TypedExpression<T>>,
) -> bool {
// we should define a heuristic here
// currently it doesn't seem like there's a tradeoff as everything gets inlined in flattening anyway (apart from compiling performance, as inlining
// in flattening should be faster and less memory intensive)
// however, using backends such as bellman, we could avoid flattening and "stream" the computation
// at proving time, the tradeoff becomes code size (not inlining keeps only one copy of each function) vs optimisation
// (inlining enables constant propagation through function calls, which cannot be achieved by our final optimiser in some cases)
// for now, we inline functions whose non-array parameters are constant, as this covers our main use case for inlining: propagation of
// constant array indices
match function {
Some(..) => {
// check whether non-array arguments are constant
arguments.iter().all(|e| match e {
TypedExpression::FieldElementArray(..) => true,
TypedExpression::FieldElement(FieldElementExpression::Number(..)) => true,
TypedExpression::Boolean(BooleanExpression::Value(..)) => true,
_ => false,
})
}
None => false,
}
}
// // inline a call to `function` taking `expressions` as inputs
// // this function mutates `self.call` by incrementing the counter for `function`, and mutates `self.context`
// fn inline_call(
// &mut self,
// function: &TypedFunction<T>,
// expressions: Vec<TypedExpression<T>>,
// ) -> Vec<TypedExpression<T>> {
// self.call_count
// .entry(function.to_slug())
// .and_modify(|i| *i += 1)
// .or_insert(1);
// self.context.push((
// function.to_slug(),
// *self.call_count.get(&function.to_slug()).unwrap(),
// ));
// inline a call to `function` taking `expressions` as inputs
// this function mutates `self.call` by incrementing the counter for `function`, and mutates `self.context`
fn inline_call(
&mut self,
function: TypedFunction<'ast, T>,
expressions: Vec<TypedExpression<'ast, T>>,
) -> Vec<TypedExpression<'ast, T>> {
self.call_count
.entry(function.to_slug())
.and_modify(|i| *i += 1)
.or_insert(1);
self.context.push((
function.id.clone(),
function.signature.clone(),
*self.call_count.get(&function.to_slug()).unwrap(),
));
// // add definitions for the inputs
// let mut inputs_bindings = function
// .arguments
// .iter()
// .zip(expressions)
// .map(|(a, e)| {
// TypedStatement::Definition(
// TypedAssignee::Identifier(self.fold_variable(a.id.clone())),
// e,
// )
// })
// .collect();
// self.statements_buffer.append(&mut inputs_bindings);
// add definitions for the inputs
let mut inputs_bindings = function
.arguments
.iter()
.zip(expressions)
.map(|(a, e)| {
TypedStatement::Definition(
TypedAssignee::Identifier(self.fold_variable(a.id.clone())),
e,
)
})
.collect();
self.statements_buffer.append(&mut inputs_bindings);
// // filter out the return statement and keep it aside
// let (mut statements, ret): (Vec<_>, Vec<_>) = function
// .statements
// .clone()
// .into_iter()
// .flat_map(|s| self.fold_statement(s))
// .partition(|s| match s {
// TypedStatement::Return(..) => false,
// _ => true,
// });
// filter out the return statement and keep it aside
let (mut statements, ret): (Vec<_>, Vec<_>) = function
.statements
.clone()
.into_iter()
.flat_map(|s| self.fold_statement(s))
.partition(|s| match s {
TypedStatement::Return(..) => false,
_ => true,
});
// // add all statements to the buffer
// self.statements_buffer.append(&mut statements);
// add all statements to the buffer
self.statements_buffer.append(&mut statements);
// // remove this call from the context
// self.context.pop();
// remove this call from the context
self.context.pop();
// match ret[0].clone() {
// TypedStatement::Return(exprs) => exprs,
// _ => panic!(""),
// }
// }
match ret[0].clone() {
TypedStatement::Return(exprs) => exprs,
_ => panic!(""),
}
}
// pub fn inline(prog: TypedProg<T>) -> TypedProg<T> {
// Inliner::new().fold_program(prog)
// }
// }
pub fn inline(prog: TypedProg<T>) -> TypedProg<T> {
Inliner::new().fold_program(prog)
}
}
// impl<T: Field> Folder<T> for Inliner<T> {
// // store the list of functions
// fn fold_program(&mut self, p: TypedProg<T>) -> TypedProg<T> {
// self.functions = p.functions.clone();
// fold_program(self, p)
// }
impl<'ast, T: Field> Folder<'ast, T> for Inliner<'ast, T> {
// store the list of functions
fn fold_program(&mut self, p: TypedProg<'ast, T>) -> TypedProg<'ast, T> {
self.functions = p.functions.clone();
fold_program(self, p)
}
// // add extra statements before the modified statement
// fn fold_statement(&mut self, s: TypedStatement<T>) -> Vec<TypedStatement<T>> {
// let mut statements = match s {
// TypedStatement::MultipleDefinition(variables, elist) => {
// match elist {
// TypedExpressionList::FunctionCall(id, exps, types) => {
// let variables: Vec<_> = variables
// .into_iter()
// .map(|a| self.fold_variable(a))
// .collect();
// let exps: Vec<_> =
// exps.into_iter().map(|e| self.fold_expression(e)).collect();
// add extra statements before the modified statement
fn fold_statement(&mut self, s: TypedStatement<'ast, T>) -> Vec<TypedStatement<'ast, T>> {
let mut statements = match s {
TypedStatement::MultipleDefinition(variables, elist) => {
match elist {
TypedExpressionList::FunctionCall(id, exps, types) => {
let variables: Vec<_> = variables
.into_iter()
.map(|a| self.fold_variable(a))
.collect();
let exps: Vec<_> =
exps.into_iter().map(|e| self.fold_expression(e)).collect();
// let passed_signature = Signature::new()
// .inputs(exps.iter().map(|e| e.get_type()).collect())
// .outputs(types.clone());
let passed_signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(types.clone());
// // find the function
// let function = self
// .functions
// .iter()
// .find(|f| f.id == id && f.signature == passed_signature)
// .cloned();
// find the function
let function = self
.functions
.iter()
.find(|f| f.id == id && f.signature == passed_signature)
.cloned();
// match self.should_inline(&function, &exps) {
// true => {
// let ret = self.inline_call(&function.unwrap(), exps);
// variables
// .into_iter()
// .zip(ret.into_iter())
// .map(|(v, e)| {
// TypedStatement::Definition(TypedAssignee::Identifier(v), e)
// })
// .collect()
// }
// false => vec![TypedStatement::MultipleDefinition(
// variables,
// TypedExpressionList::FunctionCall(id, exps, types),
// )],
// }
// }
// }
// }
// s => fold_statement(self, s),
// };
match self.should_inline(&function, &exps) {
true => {
let ret = self.inline_call(function.unwrap(), exps);
variables
.into_iter()
.zip(ret.into_iter())
.map(|(v, e)| {
TypedStatement::Definition(TypedAssignee::Identifier(v), e)
})
.collect()
}
false => vec![TypedStatement::MultipleDefinition(
variables,
TypedExpressionList::FunctionCall(id, exps, types),
)],
}
}
}
}
s => fold_statement(self, s),
};
// // add the result of folding to the buffer
// self.statements_buffer.append(&mut statements);
// // return the whole buffer
// self.statements_buffer.drain(..).collect()
// }
// add the result of folding to the buffer
self.statements_buffer.append(&mut statements);
// return the whole buffer
self.statements_buffer.drain(..).collect()
}
// // prefix all names with the context
// fn fold_name(&mut self, n: String) -> String {
// match self.context.len() {
// 0 => n,
// _ => format!(
// "{}_{}",
// self.context
// .iter()
// .map(|(s, i)| format!("{}_{}", s, i))
// .collect::<Vec<_>>()
// .join("_"),
// n
// ),
// }
// }
// prefix all names with the context
fn fold_name(&mut self, n: Identifier<'ast>) -> Identifier<'ast> {
Identifier {
stack: self.context.clone(),
..n
}
}
// // inline calls which return a field element
// fn fold_field_expression(&mut self, e: FieldElementExpression<T>) -> FieldElementExpression<T> {
// match e {
// FieldElementExpression::FunctionCall(id, exps) => {
// let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
// inline calls which return a field element
fn fold_field_expression(
&mut self,
e: FieldElementExpression<'ast, T>,
) -> FieldElementExpression<'ast, T> {
match e {
FieldElementExpression::FunctionCall(id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
// let passed_signature = Signature::new()
// .inputs(exps.iter().map(|e| e.get_type()).collect())
// .outputs(vec![Type::FieldElement]);
let passed_signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElement]);
// // find the function
// let function = self
// .functions
// .iter()
// .find(|f| f.id == id && f.signature == passed_signature)
// .cloned();
// find the function
let function = self
.functions
.iter()
.find(|f| f.id == id && f.signature == passed_signature)
.cloned();
// match self.should_inline(&function, &exps) {
// true => {
// let ret = self.inline_call(&function.unwrap(), exps);
// // unwrap the result to return a field element
// match ret[0].clone() {
// TypedExpression::FieldElement(e) => e,
// _ => panic!(""),
// }
// }
// false => FieldElementExpression::FunctionCall(id, exps),
// }
// }
// // default
// e => fold_field_expression(self, e),
// }
// }
match self.should_inline(&function, &exps) {
true => {
let ret = self.inline_call(function.unwrap(), exps);
// unwrap the result to return a field element
match ret[0].clone() {
TypedExpression::FieldElement(e) => e,
_ => panic!(""),
}
}
false => FieldElementExpression::FunctionCall(id, exps),
}
}
// default
e => fold_field_expression(self, e),
}
}
// // inline calls which return a field element array
// fn fold_field_array_expression(
// &mut self,
// e: FieldElementArrayExpression<T>,
// ) -> FieldElementArrayExpression<T> {
// match e {
// FieldElementArrayExpression::FunctionCall(size, id, exps) => {
// let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
// inline calls which return a field element array
fn fold_field_array_expression(
&mut self,
e: FieldElementArrayExpression<'ast, T>,
) -> FieldElementArrayExpression<'ast, T> {
match e {
FieldElementArrayExpression::FunctionCall(size, id, exps) => {
let exps: Vec<_> = exps.into_iter().map(|e| self.fold_expression(e)).collect();
// let passed_signature = Signature::new()
// .inputs(exps.iter().map(|e| e.get_type()).collect())
// .outputs(vec![Type::FieldElementArray(size)]);
let passed_signature = Signature::new()
.inputs(exps.iter().map(|e| e.get_type()).collect())
.outputs(vec![Type::FieldElementArray(size)]);
// // find the function
// let function = self
// .functions
// .iter()
// .find(|f| f.id == id && f.signature == passed_signature)
// .cloned();
// find the function
let function = self
.functions
.iter()
.find(|f| f.id == id && f.signature == passed_signature)
.cloned();
// match self.should_inline(&function, &exps) {
// true => {
// let ret = self.inline_call(&function.unwrap(), exps);
// // unwrap the result to return a field element
// match ret[0].clone() {
// TypedExpression::FieldElementArray(e) => e,
// _ => panic!(""),
// }
// }
// false => FieldElementArrayExpression::FunctionCall(size, id, exps),
// }
// }
// // default
// e => fold_field_array_expression(self, e),
// }
// }
// }
match self.should_inline(&function, &exps) {
true => {
let ret = self.inline_call(function.unwrap(), exps);
// unwrap the result to return a field element
match ret[0].clone() {
TypedExpression::FieldElementArray(e) => e,
_ => panic!(""),
}
}
false => FieldElementArrayExpression::FunctionCall(size, id, exps),
}
}
// default
e => fold_field_array_expression(self, e),
}
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// use zokrates_field::field::FieldPrime;
#[cfg(test)]
mod tests {
use super::*;
use zokrates_field::field::FieldPrime;
// #[cfg(test)]
// mod heuristics {
// use super::*;
#[cfg(test)]
mod heuristics {
use super::*;
// #[test]
// fn inline_constant_field() {
// let f: TypedFunction<FieldPrime> = TypedFunction {
// id: String::from("foo"),
// arguments: vec![
// Parameter::private(Variable::field_element("a")),
// Parameter::private(Variable::field_array("b", 3)),
// ],
// statements: vec![TypedStatement::Return(vec![
// FieldElementExpression::Select(
// box FieldElementArrayExpression::Identifier(3, String::from("b")),
// box FieldElementExpression::Identifier(String::from("a")),
// )
// .into(),
// ])],
// signature: Signature::new()
// .inputs(vec![Type::FieldElement, Type::FieldElementArray(3)])
// .outputs(vec![Type::FieldElement]),
// };
#[test]
fn inline_constant_field() {
let f: TypedFunction<FieldPrime> = TypedFunction {
id: "foo",
arguments: vec![
Parameter::private(Variable::field_element("a".into())),
Parameter::private(Variable::field_array("b".into(), 3)),
],
statements: vec![TypedStatement::Return(vec![
FieldElementExpression::Select(
box FieldElementArrayExpression::Identifier(3, Identifier::from("b")),
box FieldElementExpression::Identifier(Identifier::from("a")),
)
.into(),
])],
signature: Signature::new()
.inputs(vec![Type::FieldElement, Type::FieldElementArray(3)])
.outputs(vec![Type::FieldElement]),
};
// let arguments = vec![
// FieldElementExpression::Number(FieldPrime::from(0)).into(),
// FieldElementArrayExpression::Identifier(3, String::from("random")).into(),
// ];
let arguments = vec![
FieldElementExpression::Number(FieldPrime::from(0)).into(),
FieldElementArrayExpression::Identifier(3, Identifier::from("random")).into(),
];
// let i = Inliner::new();
let i = Inliner::new();
// assert!(i.should_inline(&Some(f), &arguments));
// }
assert!(i.should_inline(&Some(f), &arguments));
}
// #[test]
// fn no_inline_non_constant_field() {
// let f: TypedFunction<FieldPrime> = TypedFunction {
// id: String::from("foo"),
// arguments: vec![
// Parameter::private(Variable::field_element("a")),
// Parameter::private(Variable::field_array("b", 3)),
// ],
// statements: vec![TypedStatement::Return(vec![
// FieldElementExpression::Select(
// box FieldElementArrayExpression::Identifier(3, String::from("b")),
// box FieldElementExpression::Identifier(String::from("a")),
// )
// .into(),
// ])],
// signature: Signature::new()
// .inputs(vec![Type::FieldElement, Type::FieldElementArray(3)])
// .outputs(vec![Type::FieldElement]),
// };
#[test]
fn no_inline_non_constant_field() {
let f: TypedFunction<FieldPrime> = TypedFunction {
id: "foo",
arguments: vec![
Parameter::private(Variable::field_element("a".into())),
Parameter::private(Variable::field_array("b".into(), 3)),
],
statements: vec![TypedStatement::Return(vec![
FieldElementExpression::Select(
box FieldElementArrayExpression::Identifier(3, Identifier::from("b")),
box FieldElementExpression::Identifier(Identifier::from("a")),
)
.into(),
])],
signature: Signature::new()
.inputs(vec![Type::FieldElement, Type::FieldElementArray(3)])
.outputs(vec![Type::FieldElement]),
};
// let arguments = vec![
// FieldElementExpression::Identifier(String::from("notconstant")).into(),
// FieldElementArrayExpression::Identifier(3, String::from("random")).into(),
// ];
let arguments = vec![
FieldElementExpression::Identifier(Identifier::from("notconstant")).into(),
FieldElementArrayExpression::Identifier(3, Identifier::from("random")).into(),
];
// let i = Inliner::new();
let i = Inliner::new();
// assert!(!i.should_inline(&Some(f), &arguments));
// }
// }
// }
assert!(!i.should_inline(&Some(f), &arguments));
}
}
}

View file

@ -6,13 +6,13 @@
mod dead_code;
mod flat_propagation;
//mod inline;
mod inline;
mod power_check;
mod propagation;
mod unroll;
use self::dead_code::DeadCode;
//use self::inline::Inliner;
use self::inline::Inliner;
use self::power_check::PowerChecker;
use self::propagation::Propagator;
use self::unroll::Unroller;
@ -32,7 +32,8 @@ impl<'ast, T: Field> Analyse for TypedProg<'ast, T> {
//propagate a first time for constants to reach function calls
let r = Propagator::propagate(r);
// apply inlining strategy
//let r = Inliner::inline(r);
let r = Inliner::inline(r);
println!("{}", r);
// Propagate again
let r = Propagator::propagate(r);
// remove unused functions

View file

@ -27,10 +27,11 @@ impl<'ast> Unroller<'ast> {
id: Identifier {
id: v.id.id,
version: i + 1,
stack: vec![],
},
..v
},
None => Variable { ..v },
None => Variable { ..v.clone() },
};
self.substitution
.entry(v.id)

View file

@ -21,21 +21,38 @@ use zokrates_field::field::Field;
pub use self::folder::Folder;
#[derive(Debug, PartialEq, Clone, Hash, Eq, Copy)]
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub struct Identifier<'ast> {
pub id: &'ast str,
pub version: usize,
pub stack: Vec<(&'ast str, Signature, usize)>,
}
pub type FunctionIdentifier<'ast> = &'ast str;
impl<'ast> fmt::Display for Identifier<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}_{}", self.id, self.version)
write!(
f,
"{}_{}_{}",
self.stack
.iter()
.map(|(name, sig, count)| format!("{}_{}_{}", name, sig.to_slug(), count))
.collect::<Vec<_>>()
.join("_"),
self.id,
self.version
)
}
}
impl<'ast> From<&'ast str> for Identifier<'ast> {
fn from(id: &'ast str) -> Identifier<'ast> {
Identifier { id, version: 0 }
Identifier {
id,
version: 0,
stack: vec![],
}
}
}
@ -107,7 +124,7 @@ impl<'ast, T: Field> fmt::Debug for TypedProg<'ast, T> {
#[derive(Clone, PartialEq)]
pub struct TypedFunction<'ast, T: Field> {
/// Name of the program
pub id: String,
pub id: FunctionIdentifier<'ast>,
/// Arguments of the function
pub arguments: Vec<Parameter<'ast>>,
/// Vector of statements that are executed when running the function

View file

@ -49,6 +49,7 @@ impl<'ast> From<absy::Variable<'ast>> for Variable<'ast> {
Identifier {
id: v.id,
version: 0,
stack: vec![],
},
v._type,
)