use super::SsaConstPropagationVisitor;
use leo_ast::{
interpreter_value::{self, Value},
*,
};
use leo_errors::StaticAnalyzerError;
const VALUE_ERROR: &str = "A non-future value should always be able to be converted into an expression";
impl AstReconstructor for SsaConstPropagationVisitor<'_> {
type AdditionalInput = ();
type AdditionalOutput = Option<Value>;
fn reconstruct_path(&mut self, input: Path, _additional: &()) -> (Expression, Self::AdditionalOutput) {
let identifier_name = input.identifier().name;
if let Some(constant_value) = self.constants.get(&identifier_name).cloned() {
let span = input.span();
let id = input.id();
let (new_expr, _) = self.value_to_expression(&constant_value, span, id).expect(VALUE_ERROR);
self.changed = true;
(new_expr, Some(constant_value))
} else {
(input.into(), None)
}
}
fn reconstruct_literal(&mut self, mut input: Literal, _additional: &()) -> (Expression, Self::AdditionalOutput) {
let type_info = self.state.type_table.get(&input.id());
let type_info = type_info.as_ref().map(|ty| match ty {
Type::Optional(opt) => *opt.inner.clone(),
_ => ty.clone(),
});
if let Ok(value) = interpreter_value::literal_to_value(&input, &type_info) {
match input.variant {
LiteralVariant::Address(ref s) if s.ends_with("aleo") => {
(input.into(), None)
}
LiteralVariant::Unsuffixed(s) => {
match type_info.expect("Expected type information to be available") {
Type::Integer(ty) => input.variant = LiteralVariant::Integer(ty, s),
Type::Field => input.variant = LiteralVariant::Field(s),
Type::Group => input.variant = LiteralVariant::Group(s),
Type::Scalar => input.variant = LiteralVariant::Scalar(s),
_ => panic!("Type checking should have prevented this."),
}
(input.into(), Some(value))
}
_ => (input.into(), Some(value)),
}
} else {
(input.into(), None)
}
}
fn reconstruct_binary(
&mut self,
input: BinaryExpression,
_additional: &(),
) -> (Expression, Self::AdditionalOutput) {
let span = input.span();
let input_id = input.id();
let (left, lhs_opt_value) = self.reconstruct_expression(input.left, &());
let (right, rhs_opt_value) = self.reconstruct_expression(input.right, &());
if let (Some(lhs_value), Some(rhs_value)) = (lhs_opt_value, rhs_opt_value) {
match interpreter_value::evaluate_binary(
span,
input.op,
&lhs_value,
&rhs_value,
&self.state.type_table.get(&input_id),
) {
Ok(new_value) => {
let (new_expr, _) = self.value_to_expression(&new_value, span, input_id).expect(VALUE_ERROR);
self.changed = true;
return (new_expr, Some(new_value));
}
Err(err) => self
.emit_err(StaticAnalyzerError::compile_time_binary_op(lhs_value, rhs_value, input.op, err, span)),
}
}
(BinaryExpression { left, right, ..input }.into(), None)
}
fn reconstruct_unary(&mut self, input: UnaryExpression, _additional: &()) -> (Expression, Self::AdditionalOutput) {
let input_id = input.id();
let span = input.span;
let (receiver, opt_value) = self.reconstruct_expression(input.receiver, &());
if let Some(value) = opt_value {
match interpreter_value::evaluate_unary(span, input.op, &value, &self.state.type_table.get(&input_id)) {
Ok(new_value) => {
let (new_expr, _) = self.value_to_expression(&new_value, span, input_id).expect(VALUE_ERROR);
self.changed = true;
return (new_expr, Some(new_value));
}
Err(err) => self.emit_err(StaticAnalyzerError::compile_time_unary_op(value, input.op, err, span)),
}
}
(UnaryExpression { receiver, ..input }.into(), None)
}
fn reconstruct_ternary(
&mut self,
input: TernaryExpression,
_additional: &(),
) -> (Expression, Self::AdditionalOutput) {
let (cond, cond_value) = self.reconstruct_expression(input.condition, &());
match cond_value.and_then(|v| v.try_into().ok()) {
Some(true) => {
self.changed = true;
self.reconstruct_expression(input.if_true, &())
}
Some(false) => {
self.changed = true;
self.reconstruct_expression(input.if_false, &())
}
_ => (
TernaryExpression {
condition: cond,
if_true: self.reconstruct_expression(input.if_true, &()).0,
if_false: self.reconstruct_expression(input.if_false, &()).0,
..input
}
.into(),
None,
),
}
}
fn reconstruct_array_access(
&mut self,
input: ArrayAccess,
_additional: &(),
) -> (Expression, Self::AdditionalOutput) {
let span = input.span();
let id = input.id();
let (array, array_opt) = self.reconstruct_expression(input.array, &());
let (index, index_opt) = self.reconstruct_expression(input.index, &());
if let Some(index_value) = index_opt
&& let Some(array_value) = array_opt
{
let result_value =
array_value.array_index(index_value.as_u32().unwrap() as usize).expect("We already checked bounds.");
self.changed = true;
let (new_expr, _) = self.value_to_expression(&result_value, span, id).expect(VALUE_ERROR);
return (new_expr, Some(result_value.clone()));
}
(ArrayAccess { array, index, ..input }.into(), None)
}
fn reconstruct_array(
&mut self,
mut input: ArrayExpression,
_additional: &(),
) -> (Expression, Self::AdditionalOutput) {
let mut values = Vec::new();
let mut elements_changed = false;
input.elements.iter_mut().for_each(|element| {
let old_element = element.clone();
let (new_element, value_opt) = self.reconstruct_expression(std::mem::take(element), &());
if old_element.id() != new_element.id() {
elements_changed = true;
}
if let Some(value) = value_opt {
values.push(value);
}
*element = new_element;
});
if elements_changed {
self.changed = true;
}
if values.len() == input.elements.len() {
(input.into(), Some(Value::make_array(values.into_iter())))
} else {
(input.into(), None)
}
}
fn reconstruct_tuple(
&mut self,
mut input: TupleExpression,
_additional: &(),
) -> (Expression, Self::AdditionalOutput) {
let mut values = Vec::with_capacity(input.elements.len());
let mut elements_changed = false;
for expr in input.elements.iter_mut() {
let old_expr = expr.clone();
let (new_expr, opt_value) = self.reconstruct_expression(std::mem::take(expr), &());
if old_expr.id() != new_expr.id() {
elements_changed = true;
}
*expr = new_expr;
if let Some(value) = opt_value {
values.push(value);
}
}
if elements_changed {
self.changed = true;
}
let opt_value = if values.len() == input.elements.len() { Some(Value::make_tuple(values)) } else { None };
(input.into(), opt_value)
}
fn reconstruct_definition(&mut self, mut input: DefinitionStatement) -> (Statement, Self::AdditionalOutput) {
let (new_value, opt_value) = self.reconstruct_expression(input.value, &());
if let Some(value) = opt_value {
match &input.place {
DefinitionPlace::Single(identifier) => {
self.constants.insert(identifier.name, value);
}
DefinitionPlace::Multiple(identifiers) => {
for (i, id) in identifiers.iter().enumerate() {
if let Some(v) = value.tuple_index(i) {
self.constants.insert(id.name, v);
}
}
}
}
}
input.value = new_value;
(input.into(), None)
}
fn reconstruct_assign(&mut self, _input: AssignStatement) -> (Statement, Self::AdditionalOutput) {
panic!("there should be no assignments at this stage");
}
}