1
0
Fork 0
mirror of synced 2025-09-23 20:28:36 +00:00

implement shadowing only at the semantic phase

This commit is contained in:
schaeff 2022-07-21 17:52:54 +02:00
parent 8f7b124123
commit b2cf1012e8
14 changed files with 536 additions and 322 deletions

View file

@ -128,14 +128,14 @@ pub trait Folder<'ast, T: Field>: Sized {
}
fn fold_name(&mut self, n: Identifier<'ast>) -> Identifier<'ast> {
let id = match n.id {
let id = match n.id.id {
CoreIdentifier::Constant(c) => {
CoreIdentifier::Constant(self.fold_canonical_constant_identifier(c))
}
id => id,
};
Identifier { id, ..n }
Identifier { id: ShadowedIdentifier {id, ..n.id}, ..n }
}
fn fold_variable(&mut self, v: Variable<'ast, T>) -> Variable<'ast, T> {

View file

@ -1,7 +1,8 @@
use crate::typed::CanonicalConstantIdentifier;
use std::convert::TryInto;
use std::fmt;
pub type SourceIdentifier<'ast> = &'ast str;
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
pub enum CoreIdentifier<'ast> {
Source(&'ast str),
@ -36,23 +37,56 @@ impl<'ast> From<CanonicalConstantIdentifier<'ast>> for CoreIdentifier<'ast> {
/// A identifier for a variable
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
pub struct Identifier<'ast> {
/// the id of the variable
pub id: CoreIdentifier<'ast>,
/// the id of the variable with its shadowing id
pub id: ShadowedIdentifier<'ast>,
/// the version of the variable, used after SSA transformation
pub version: usize,
}
impl<'ast> TryInto<&'ast str> for Identifier<'ast> {
type Error = ();
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
pub struct ShadowedIdentifier<'ast> {
pub id: CoreIdentifier<'ast>,
pub shadow: usize,
}
fn try_into(self) -> Result<&'ast str, Self::Error> {
match self.id {
CoreIdentifier::Source(i) => Ok(i),
_ => Err(()),
impl<'ast> ShadowedIdentifier<'ast> {
pub fn top_level(id: CoreIdentifier<'ast>) -> Self {
Self::nth(id, 0)
}
pub fn function_level(id: CoreIdentifier<'ast>) -> Self {
Self::nth(id, 1)
}
pub fn nth(id: CoreIdentifier<'ast>, shadow: usize) -> Self {
Self {
id,
shadow
}
}
}
impl<'ast> fmt::Display for ShadowedIdentifier<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.shadow == 0 {
write!(f, "{}", self.id)
} else {
write!(f, "{}_{}", self.id, self.shadow)
}
}
}
// impl<'ast> TryInto<&'ast str> for Identifier<'ast> {
// type Error = ();
// fn try_into(self) -> Result<&'ast str, Self::Error> {
// match self.id {
// CoreIdentifier::Source(i) => Ok(i),
// _ => Err(()),
// }
// }
// }
impl<'ast> fmt::Display for Identifier<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.version == 0 {
@ -65,19 +99,37 @@ impl<'ast> fmt::Display for Identifier<'ast> {
impl<'ast> From<CanonicalConstantIdentifier<'ast>> for Identifier<'ast> {
fn from(id: CanonicalConstantIdentifier<'ast>) -> Identifier<'ast> {
Identifier::from(CoreIdentifier::Constant(id))
Identifier::from(ShadowedIdentifier { id: CoreIdentifier::Constant(id), shadow: 0})
}
}
impl<'ast> From<&'ast str> for Identifier<'ast> {
fn from(id: &'ast str) -> Identifier<'ast> {
Identifier::from(CoreIdentifier::Source(id))
Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Source(id)))
}
}
impl<'ast> From<CoreIdentifier<'ast>> for ShadowedIdentifier<'ast> {
fn from(id: CoreIdentifier<'ast>) -> ShadowedIdentifier<'ast> {
Self::nth(id, 1)
}
}
impl<'ast> From<&'ast str> for ShadowedIdentifier<'ast> {
fn from(id: &'ast str) -> ShadowedIdentifier<'ast> {
Self::nth(id.into(), 1)
}
}
impl<'ast> From<ShadowedIdentifier<'ast>> for Identifier<'ast> {
fn from(id: ShadowedIdentifier<'ast>) -> Identifier<'ast> {
Identifier { id, version: 0 }
}
}
impl<'ast> From<CoreIdentifier<'ast>> for Identifier<'ast> {
fn from(id: CoreIdentifier<'ast>) -> Identifier<'ast> {
Identifier { id, version: 0 }
Identifier::from(ShadowedIdentifier::from(id))
}
}

View file

@ -16,7 +16,7 @@ pub mod types;
mod uint;
pub mod variable;
pub use self::identifier::CoreIdentifier;
pub use self::identifier::{CoreIdentifier, SourceIdentifier, ShadowedIdentifier};
pub use self::parameter::{DeclarationParameter, GParameter};
pub use self::types::{
CanonicalConstantIdentifier, ConcreteFunctionKey, ConcreteSignature, ConcreteTupleType,

View file

@ -156,14 +156,14 @@ pub trait ResultFolder<'ast, T: Field>: Sized {
}
fn fold_name(&mut self, n: Identifier<'ast>) -> Result<Identifier<'ast>, Self::Error> {
let id = match n.id {
let id = match n.id.id {
CoreIdentifier::Constant(c) => {
CoreIdentifier::Constant(self.fold_canonical_constant_identifier(c)?)
}
id => id,
};
Ok(Identifier { id, ..n })
Ok(Identifier { id: ShadowedIdentifier {id, ..n.id}, ..n })
}
fn fold_variable(&mut self, v: Variable<'ast, T>) -> Result<Variable<'ast, T>, Self::Error> {

View file

@ -229,13 +229,13 @@ impl<'ast, T> From<DeclarationConstant<'ast, T>> for UExpression<'ast, T> {
fn from(c: DeclarationConstant<'ast, T>) -> Self {
match c {
DeclarationConstant::Generic(i) => {
UExpressionInner::Identifier(i.name().into()).annotate(UBitwidth::B32)
UExpressionInner::Identifier(ShadowedIdentifier::function_level(i.name().into()).into()).annotate(UBitwidth::B32)
}
DeclarationConstant::Concrete(v) => {
UExpressionInner::Value(v as u128).annotate(UBitwidth::B32)
}
DeclarationConstant::Constant(v) => {
UExpressionInner::Identifier(CoreIdentifier::from(v).into())
UExpressionInner::Identifier(ShadowedIdentifier::top_level(CoreIdentifier::from(v)).into())
.annotate(UBitwidth::B32)
}
DeclarationConstant::Expression(e) => e.try_into().unwrap(),
@ -1129,7 +1129,7 @@ pub fn check_type<'ast, T, S: Clone + PartialEq + PartialEq<u32>>(
impl<'ast, T> From<CanonicalConstantIdentifier<'ast>> for UExpression<'ast, T> {
fn from(c: CanonicalConstantIdentifier<'ast>) -> Self {
UExpressionInner::Identifier(Identifier::from(CoreIdentifier::Constant(c)))
UExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c))))
.annotate(UBitwidth::B32)
}
}
@ -1215,6 +1215,8 @@ pub use self::signature::{
try_from_g_signature, ConcreteSignature, DeclarationSignature, GSignature, Signature,
};
use super::ShadowedIdentifier;
pub mod signature {
use super::*;
use std::fmt;

View file

@ -127,11 +127,11 @@ impl<'ast, T: Field> From<u128> for UExpressionInner<'ast, T> {
}
}
impl<'ast, T: Field> From<&'ast str> for UExpressionInner<'ast, T> {
fn from(e: &'ast str) -> Self {
UExpressionInner::Identifier(e.into())
}
}
// impl<'ast, T: Field> From<&'ast str> for UExpressionInner<'ast, T> {
// fn from(e: &'ast str) -> Self {
// UExpressionInner::Identifier(e.into())
// }
// }
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct UMetadata {

View file

@ -84,12 +84,6 @@ impl<'ast, T: Field> From<u128> for UExpressionInner<'ast, T> {
}
}
impl<'ast, T: Field> From<&'ast str> for UExpressionInner<'ast, T> {
fn from(e: &'ast str) -> Self {
UExpressionInner::Identifier(e.into())
}
}
impl<'ast, T> From<u32> for UExpression<'ast, T> {
fn from(u: u32) -> Self {
UExpressionInner::Value(u as u128).annotate(UBitwidth::B32)

View file

@ -5,11 +5,12 @@
//! @date 2017
use num_bigint::BigUint;
use std::collections::{btree_map::Entry, hash_map, BTreeMap, BTreeSet, HashMap, HashSet};
use std::collections::{btree_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt;
use std::path::PathBuf;
use zokrates_ast::common::FormatString;
use zokrates_ast::typed::types::{GGenericsAssignment, GTupleType, GenericsAssignment};
use zokrates_ast::typed::SourceIdentifier;
use zokrates_ast::typed::*;
use zokrates_ast::typed::{DeclarationParameter, DeclarationVariable, Variable};
use zokrates_ast::untyped::Identifier;
@ -18,7 +19,7 @@ use zokrates_field::Field;
use zokrates_ast::untyped::types::{UnresolvedSignature, UnresolvedType, UserTypeId};
use std::hash::{Hash, Hasher};
use std::hash::Hash;
use zokrates_ast::typed::types::{
check_type, specialize_declaration_type, ArrayType, DeclarationArrayType, DeclarationConstant,
DeclarationFunctionKey, DeclarationSignature, DeclarationStructMember, DeclarationStructType,
@ -262,65 +263,57 @@ impl<'ast, T: Field> FunctionQuery<'ast, T> {
}
}
/// A scoped variable, so that we can delete all variables of a given scope when exiting it
#[derive(Clone, Debug)]
pub struct ScopedIdentifier<'ast> {
id: CoreIdentifier<'ast>,
level: usize,
}
impl<'ast> ScopedIdentifier<'ast> {
fn is_constant(&self) -> bool {
let res = self.level == 0;
// currently this is encoded twice: in the level and in the identifier itself.
// we add a sanity check to make sure the two agree
assert_eq!(res, matches!(self.id, CoreIdentifier::Constant(..)));
res
}
}
/// Identifiers coming from constants or variables are equivalent
impl<'ast> PartialEq for ScopedIdentifier<'ast> {
fn eq(&self, other: &Self) -> bool {
let i0 = match &self.id {
CoreIdentifier::Source(id) => id,
CoreIdentifier::Constant(c) => c.id,
_ => unreachable!(),
};
let i1 = match &other.id {
CoreIdentifier::Source(id) => id,
CoreIdentifier::Constant(c) => c.id,
_ => unreachable!(),
};
i0 == i1 && (self.level == other.level)
}
}
/// Identifiers coming from constants or variables hash to the same result
impl<'ast> Hash for ScopedIdentifier<'ast> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.level.hash(state);
match &self.id {
CoreIdentifier::Source(id) => id.hash(state),
CoreIdentifier::Constant(c) => c.id.hash(state),
_ => unreachable!(),
};
}
}
impl<'ast> Eq for ScopedIdentifier<'ast> {}
#[derive(Debug)]
pub struct IdentifierInfo<'ast, T> {
#[derive(Debug, Clone)]
pub struct IdentifierInfo<'ast, T, U> {
id: U,
ty: Type<'ast, T>,
is_mutable: bool,
}
type Scope<'ast, T> = HashMap<ScopedIdentifier<'ast>, IdentifierInfo<'ast, T>>;
#[derive(Default, Debug)]
struct Scope<'ast, T> {
level: usize,
map: HashMap<SourceIdentifier<'ast>, BTreeMap<usize, IdentifierInfo<'ast, T, CoreIdentifier<'ast>>>>,
}
impl<'ast, T: Field> Scope<'ast, T> {
// insert into the scope and return how many existing variables we are shadowing
fn insert(&mut self, id: SourceIdentifier<'ast>, info: IdentifierInfo<'ast, T, CoreIdentifier<'ast>>) -> bool {
let existed = self
.map
.get(&id)
.map(|versions| !versions.is_empty())
.unwrap_or(false);
self.map.entry(id).or_default().insert(self.level, info);
existed
}
/// get the current version of this variable
fn get(&self, id: &SourceIdentifier<'ast>) -> Option<IdentifierInfo<'ast, T, ShadowedIdentifier<'ast>>> {
self.map
.get(id)
.and_then(|versions| {
let (n, info) = versions.iter().next_back()?;
Some(IdentifierInfo {
id: ShadowedIdentifier::nth(info.id.clone(), *n),
ty: info.ty.clone(),
is_mutable: info.is_mutable
})
})
}
fn enter(&mut self) {
self.level += 1;
}
fn exit(&mut self) {
for versions in self.map.values_mut() {
versions.remove(&self.level);
}
self.level -= 1;
}
}
/// Checker checks the semantics of a program, keeping track of functions and variables in scope
#[derive(Default)]
@ -328,7 +321,6 @@ pub struct Checker<'ast, T> {
return_type: Option<DeclarationType<'ast, T>>,
scope: Scope<'ast, T>,
functions: HashSet<DeclarationFunctionKey<'ast, T>>,
level: usize,
}
impl<'ast, T: Field> Checker<'ast, T> {
@ -693,13 +685,18 @@ impl<'ast, T: Field> Checker<'ast, T> {
)
.into(),
);
self.insert_into_scope(Variable::immutable(
CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
let id = declaration.id;
let info = IdentifierInfo {
id: CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
declaration.id,
module_id.into(),
)),
c.get_type(),
));
ty: c.get_type(),
is_mutable: false,
};
assert_eq!(self.scope.level, 0);
assert!(!self.scope.insert(id, info));
assert!(state
.constants
.entry(module_id.to_path_buf())
@ -884,10 +881,18 @@ impl<'ast, T: Field> Checker<'ast, T> {
id.clone(),
TypedConstantSymbol::There(imported_id)
).into());
self.insert_into_scope(Variable::immutable(CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
let id = declaration.id;
let info = IdentifierInfo {
id: CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
declaration.id,
module_id.into(),
)), zokrates_ast::typed::types::try_from_g_type(ty.clone()).unwrap()));
)),
ty: zokrates_ast::typed::types::try_from_g_type(ty.clone()).unwrap(),
is_mutable: false,
};
assert_eq!(self.scope.level, 0);
assert!(!self.scope.insert(id, info));
state
.constants
@ -1072,8 +1077,6 @@ impl<'ast, T: Field> Checker<'ast, T> {
}]),
}?;
self.insert_into_scope(var.clone());
Ok(var)
}
@ -1112,17 +1115,14 @@ impl<'ast, T: Field> Checker<'ast, T> {
};
// for declaration signatures, generics cannot be ignored
let v = Variable::immutable(generic.name(), Type::Uint(UBitwidth::B32));
generics.0.insert(
generic.clone(),
UExpressionInner::Identifier(generic.name().into())
UExpressionInner::Identifier(ShadowedIdentifier::nth(generic.name().into(), self.scope.level).into())
.annotate(UBitwidth::B32),
);
//we don't have to check for conflicts here, because this was done when checking the signature
self.insert_into_scope(v.clone());
self.insert_into_scope(generic.name(), Type::Uint(UBitwidth::B32), false);
}
for (arg, decl_ty) in funct.arguments.into_iter().zip(s.inputs.iter()) {
@ -1131,7 +1131,7 @@ impl<'ast, T: Field> Checker<'ast, T> {
let arg = arg.value;
let decl_v = DeclarationVariable::new(
arg.id.value.id,
ShadowedIdentifier::nth(arg.id.value.id.into(), 1),
decl_ty.clone(),
arg.id.value.is_mutable,
);
@ -1140,16 +1140,20 @@ impl<'ast, T: Field> Checker<'ast, T> {
let ty = specialize_declaration_type(decl_v.clone()._type, &generics).unwrap();
match self.insert_into_scope(zokrates_ast::typed::variable::Variable {
id: decl_v.clone().id,
_type: ty,
assert_eq!(self.scope.level, 1);
let id = arg.id.value.id;
let info = IdentifierInfo {
id: decl_v.id.id.id.clone(),
ty,
is_mutable,
}) {
true => {}
false => {
};
match self.scope.insert(id, info) {
false => {}
true => {
errors.push(ErrorInner {
pos: Some(pos),
message: format!("Duplicate name in function definition: `{}` was previously declared as an argument or a generic constant", arg.id.value.id)
message: format!("Duplicate name in function definition: `{}` was previously declared as an argument, a generic parameter or a constant", arg.id.value.id)
});
}
};
@ -1623,10 +1627,16 @@ impl<'ast, T: Field> Checker<'ast, T> {
module_id: &ModuleId,
types: &TypeMap<'ast, T>,
) -> Result<Variable<'ast, T>, Vec<ErrorInner>> {
let ty = self.check_type(v.value._type, module_id, types)
.map_err(|e| vec![e])?;
// insert into the scope and ignore whether shadowing happened
self.insert_into_scope(v.value.id, ty.clone(), v.value.is_mutable);
Ok(Variable::new(
v.value.id,
self.check_type(v.value._type, module_id, types)
.map_err(|e| vec![e])?,
ShadowedIdentifier::nth(v.value.id.into(), self.scope.level),
ty,
v.value.is_mutable,
))
}
@ -1718,71 +1728,54 @@ impl<'ast, T: Field> Checker<'ast, T> {
}
// the assignee is already checked to be defined and mutable
fn check_assignment_inner(
fn check_rhs(
&mut self,
assignee: TypedAssignee<'ast, T>,
return_type: Type<'ast, T>,
expr: ExpressionNode<'ast>,
statement_pos: (Position, Position),
module_id: &ModuleId,
types: &TypeMap<'ast, T>,
) -> Result<TypedStatement<'ast, T>, Vec<ErrorInner>> {
let assignee_type = assignee.get_type();
) -> Result<TypedExpression<'ast, T>, ErrorInner> {
match expr.value {
Expression::FunctionCall(box fun_id_expression, generics, arguments) => {
let e = self
self
.check_function_call_expression(
fun_id_expression,
generics,
arguments,
Some(assignee_type),
Some(return_type),
module_id,
types,
)
.map_err(|e| vec![e])?;
Ok(TypedStatement::Definition(assignee, e))
}
_ => {
// check the expression to be assigned
let checked_expr = self
self
.check_expression(expr, module_id, types)
.map_err(|e| vec![e])?;
// make sure the assignee has the same type as the rhs
match assignee_type {
Type::FieldElement => FieldElementExpression::try_from_typed(checked_expr)
.map(TypedExpression::from),
Type::Boolean => {
BooleanExpression::try_from_typed(checked_expr).map(TypedExpression::from)
}
Type::Uint(bitwidth) => UExpression::try_from_typed(checked_expr, &bitwidth)
.map(TypedExpression::from),
Type::Array(ref array_ty) => {
ArrayExpression::try_from_typed(checked_expr, array_ty)
.map(TypedExpression::from)
}
Type::Struct(ref struct_ty) => {
StructExpression::try_from_typed(checked_expr, struct_ty)
.map(TypedExpression::from)
}
Type::Tuple(ref tuple_ty) => {
TupleExpression::try_from_typed(checked_expr, tuple_ty)
.map(TypedExpression::from)
}
Type::Int => Err(checked_expr), // Integers cannot be assigned
}
.map_err(|e| ErrorInner {
pos: Some(statement_pos),
message: format!(
"Expression `{}` of type `{}` cannot be assigned to `{}` of type `{}`",
e,
e.get_type(),
assignee.clone(),
assignee_type
),
})
.map(|rhs| TypedStatement::Definition(assignee, rhs))
.map_err(|e| vec![e])
// // make sure the assignee has the same type as the rhs
// match return_type {
// Type::FieldElement => FieldElementExpression::try_from_typed(checked_expr)
// .map(TypedExpression::from),
// Type::Boolean => {
// BooleanExpression::try_from_typed(checked_expr).map(TypedExpression::from)
// }
// Type::Uint(bitwidth) => UExpression::try_from_typed(checked_expr, &bitwidth)
// .map(TypedExpression::from),
// Type::Array(ref array_ty) => {
// ArrayExpression::try_from_typed(checked_expr, array_ty)
// .map(TypedExpression::from)
// }
// Type::Struct(ref struct_ty) => {
// StructExpression::try_from_typed(checked_expr, struct_ty)
// .map(TypedExpression::from)
// }
// Type::Tuple(ref tuple_ty) => {
// TupleExpression::try_from_typed(checked_expr, tuple_ty)
// .map(TypedExpression::from)
// }
// Type::Int => Err(checked_expr), // Integers cannot be assigned
// }
// .map_err(|e| vec![e])
}
}
}
@ -1896,27 +1889,102 @@ impl<'ast, T: Field> Checker<'ast, T> {
Ok(res)
}
Statement::Definition(var, expr) => {
let var = self.check_variable(var, module_id, types)?;
match self.insert_into_scope(var.clone()) {
true => Ok(()),
false => Err(ErrorInner {
pos: Some(pos),
message: format!("Duplicate declaration for variable named {}", var.id),
}),
}
// get the lhs type
let var_ty = self.check_type(var.value._type, module_id, types)
.map_err(|e| vec![e])?;
let assignee = TypedAssignee::Identifier(var);
// check the rhs based on the lhs type
let checked_expr = self.check_rhs(var_ty.clone(), expr, module_id, types).map_err(|e| vec![e])?;
self.check_assignment_inner(assignee, expr, pos, module_id, types)
// insert the lhs into the scope and ignore whether shadowing happened
self.insert_into_scope(var.value.id, var_ty.clone(), var.value.is_mutable);
let var = Variable::new(
ShadowedIdentifier::nth(var.value.id.into(), self.scope.level),
var_ty.clone(),
var.value.is_mutable,
);
match var_ty {
Type::FieldElement => FieldElementExpression::try_from_typed(checked_expr)
.map(TypedExpression::from),
Type::Boolean => {
BooleanExpression::try_from_typed(checked_expr).map(TypedExpression::from)
}
Type::Uint(bitwidth) => UExpression::try_from_typed(checked_expr, &bitwidth)
.map(TypedExpression::from),
Type::Array(ref array_ty) => {
ArrayExpression::try_from_typed(checked_expr, array_ty)
.map(TypedExpression::from)
}
Type::Struct(ref struct_ty) => {
StructExpression::try_from_typed(checked_expr, struct_ty)
.map(TypedExpression::from)
}
Type::Tuple(ref tuple_ty) => {
TupleExpression::try_from_typed(checked_expr, tuple_ty)
.map(TypedExpression::from)
}
Type::Int => Err(checked_expr), // Integers cannot be assigned
}
.map_err(|e| ErrorInner {
pos: Some(pos),
message: format!(
"Expression `{}` of type `{}` cannot be assigned to `{}` of type `{}`",
e,
e.get_type(),
var.clone(),
var_ty
),
})
.map(|e| TypedStatement::Definition(var.into(), e))
.map_err(|e| vec![e])
}
Statement::Assignment(assignee, expr) => {
// check that the assignee is declared, well formed and mutable
let var = self
let assignee = self
.check_assignee(assignee, module_id, types)
.map_err(|e| vec![e])?;
self.check_assignment_inner(var, expr, pos, module_id, types)
let assignee_ty = assignee.get_type();
let checked_expr = self.check_rhs(assignee_ty.clone(), expr, module_id, types).map_err(|e| vec![e])?;
match assignee_ty {
Type::FieldElement => FieldElementExpression::try_from_typed(checked_expr)
.map(TypedExpression::from),
Type::Boolean => {
BooleanExpression::try_from_typed(checked_expr).map(TypedExpression::from)
}
Type::Uint(bitwidth) => UExpression::try_from_typed(checked_expr, &bitwidth)
.map(TypedExpression::from),
Type::Array(ref array_ty) => {
ArrayExpression::try_from_typed(checked_expr, array_ty)
.map(TypedExpression::from)
}
Type::Struct(ref struct_ty) => {
StructExpression::try_from_typed(checked_expr, struct_ty)
.map(TypedExpression::from)
}
Type::Tuple(ref tuple_ty) => {
TupleExpression::try_from_typed(checked_expr, tuple_ty)
.map(TypedExpression::from)
}
Type::Int => Err(checked_expr), // Integers cannot be assigned
}
.map_err(|e| ErrorInner {
pos: Some(pos),
message: format!(
"Expression `{}` of type `{}` cannot be assigned to `{}` of type `{}`",
e,
e.get_type(),
assignee.clone(),
assignee_ty
),
})
.map(|e| TypedStatement::Definition(assignee, e))
.map_err(|e| vec![e])
}
Statement::Assertion(e, message) => {
let e = self
@ -1964,18 +2032,14 @@ impl<'ast, T: Field> Checker<'ast, T> {
let pos = assignee.pos();
// check that the assignee is declared
match assignee.value {
Assignee::Identifier(variable_name) => match self.get_key_value_scope(variable_name) {
Some((id, info)) => match (id.is_constant(), info.is_mutable) {
(true, _) => Err(ErrorInner {
pos: Some(assignee.pos()),
message: format!("Assignment to constant variable `{}`", variable_name),
}),
(_, false) => Err(ErrorInner {
Assignee::Identifier(variable_name) => match self.scope.get(&variable_name) {
Some(info) => match info.is_mutable {
false => Err(ErrorInner {
pos: Some(assignee.pos()),
message: format!("Assignment to an immutable variable `{}`", variable_name),
}),
_ => Ok(TypedAssignee::Identifier(Variable::new(
variable_name,
info.id,
info.ty.clone(),
info.is_mutable,
))),
@ -2277,32 +2341,34 @@ impl<'ast, T: Field> Checker<'ast, T> {
Expression::BooleanConstant(b) => Ok(BooleanExpression::Value(b).into()),
Expression::Identifier(name) => {
// check that `id` is defined in the scope
match self
.get_key_value_scope(name)
.map(|(x, y)| (x.clone(), y.ty.clone()))
{
Some((id, ty)) => match ty {
Type::Boolean => Ok(BooleanExpression::Identifier(id.id.into()).into()),
Type::Uint(bitwidth) => Ok(UExpressionInner::Identifier(id.id.into())
match self.scope.get(&name) {
Some(info) => {
let id = info.id;
match info.ty.clone() {
Type::Boolean => Ok(BooleanExpression::Identifier(id.into()).into()),
Type::Uint(bitwidth) => Ok(UExpressionInner::Identifier(id.into())
.annotate(bitwidth)
.into()),
Type::FieldElement => {
Ok(FieldElementExpression::Identifier(id.id.into()).into())
Ok(FieldElementExpression::Identifier(id.into()).into())
}
Type::Array(array_type) => {
Ok(ArrayExpressionInner::Identifier(id.id.into())
Ok(ArrayExpressionInner::Identifier(id.into())
.annotate(*array_type.ty, *array_type.size)
.into())
}
Type::Struct(members) => {
Ok(StructExpressionInner::Identifier(id.id.into())
Ok(StructExpressionInner::Identifier(id.into())
.annotate(members)
.into())
}
Type::Tuple(tuple_ty) => Ok(TupleExpressionInner::Identifier(id.id.into())
Type::Tuple(tuple_ty) => {
Ok(TupleExpressionInner::Identifier(id.into())
.annotate(tuple_ty)
.into()),
.into())
}
Type::Int => unreachable!(),
}
},
None => Err(ErrorInner {
pos: Some(pos),
@ -3537,36 +3603,13 @@ impl<'ast, T: Field> Checker<'ast, T> {
}
}
fn get_key_value_scope<'a>(
&'a self,
identifier: &'ast str,
) -> Option<(&'a ScopedIdentifier<'ast>, &'a IdentifierInfo<'ast, T>)> {
self.scope.get_key_value(&ScopedIdentifier {
id: CoreIdentifier::Source(identifier),
level: self.level,
})
}
fn insert_into_scope(&mut self, v: Variable<'ast, T>) -> bool {
let id = ScopedIdentifier {
id: v.id.id,
level: self.level,
};
fn insert_into_scope(&mut self, id: SourceIdentifier<'ast>, ty: Type<'ast, T>, is_mutable: bool) -> bool {
let info = IdentifierInfo {
ty: v._type,
is_mutable: v.is_mutable,
id: CoreIdentifier::Source(id),
ty,
is_mutable,
};
match self.scope.entry(id) {
hash_map::Entry::Occupied(mut e) => {
// we shadow here any previously declared variables
e.insert(info);
}
hash_map::Entry::Vacant(e) => {
// first time declared
e.insert(info);
}
};
true // TODO: remove this
self.scope.insert(id, info)
}
fn find_functions(
@ -3577,14 +3620,11 @@ impl<'ast, T: Field> Checker<'ast, T> {
}
fn enter_scope(&mut self) {
self.level += 1;
self.scope.enter();
}
fn exit_scope(&mut self) {
let current_level = self.level;
self.scope
.retain(|scoped_variable, _| scoped_variable.level < current_level);
self.level -= 1;
self.scope.exit();
}
}
@ -4305,15 +4345,13 @@ mod tests {
}
}
pub fn new_with_args<'ast, T: Field>(
fn new_with_args<'ast, T: Field>(
scope: Scope<'ast, T>,
level: usize,
functions: HashSet<DeclarationFunctionKey<'ast, T>>,
) -> Checker<'ast, T> {
Checker {
scope,
functions,
level,
return_type: None,
}
}
@ -4425,21 +4463,20 @@ mod tests {
let mut scope = Scope::default();
scope.insert(
ScopedIdentifier {
id: CoreIdentifier::Source("b"),
level: 1,
},
"b",
IdentifierInfo {
id: CoreIdentifier::Source("b"),
ty: Type::FieldElement,
is_mutable: false,
},
);
let mut checker: Checker<Bn128Field> = new_with_args(scope, 1, HashSet::new());
let mut checker: Checker<Bn128Field> = new_with_args(scope, HashSet::new());
checker.enter_scope();
assert_eq!(
checker.check_statement(statement, &*MODULE_ID, &TypeMap::new()),
Ok(TypedStatement::Definition(
TypedAssignee::Identifier(typed::Variable::field_element("a")),
typed::Variable::field_element("a").into(),
FieldElementExpression::Identifier("b".into()).into()
))
);
@ -4668,7 +4705,7 @@ mod tests {
];
let for_statements_checked = vec![TypedStatement::Definition(
TypedAssignee::Identifier(typed::Variable::uint("a", UBitwidth::B32)),
typed::Variable::uint("a", UBitwidth::B32).into(),
UExpressionInner::Identifier("i".into())
.annotate(UBitwidth::B32)
.into(),
@ -4751,7 +4788,7 @@ mod tests {
let modules = Modules::new();
let state = State::new(modules);
let mut checker: Checker<Bn128Field> = new_with_args(HashMap::new(), 0, functions);
let mut checker: Checker<Bn128Field> = new_with_args(Scope::default(), functions);
assert_eq!(
checker.check_function(bar, &*MODULE_ID, &state),
Err(vec![ErrorInner {
@ -4790,7 +4827,7 @@ mod tests {
let modules = Modules::new();
let state = State::new(modules);
let mut checker: Checker<Bn128Field> = new_with_args(HashMap::new(), 0, HashSet::new());
let mut checker: Checker<Bn128Field> = new_with_args(Scope::default(), HashSet::new());
assert_eq!(
checker.check_function(bar, &*MODULE_ID, &state),
Err(vec![ErrorInner {
@ -4863,7 +4900,7 @@ mod tests {
let mut state =
State::<Bn128Field>::new(vec![((*MODULE_ID).clone(), module)].into_iter().collect());
let mut checker: Checker<Bn128Field> = new_with_args(HashMap::new(), 0, HashSet::new());
let mut checker: Checker<Bn128Field> = new_with_args(Scope::default(), HashSet::new());
assert_eq!(
checker.check_module(&*MODULE_ID, &mut state),
Err(vec![Error {
@ -4959,7 +4996,7 @@ mod tests {
let mut state =
State::<Bn128Field>::new(vec![((*MODULE_ID).clone(), module)].into_iter().collect());
let mut checker: Checker<Bn128Field> = new_with_args(HashMap::new(), 0, HashSet::new());
let mut checker: Checker<Bn128Field> = new_with_args(Scope::default(), HashSet::new());
assert!(checker.check_module(&*MODULE_ID, &mut state).is_ok());
}
@ -4999,7 +5036,7 @@ mod tests {
let modules = Modules::new();
let state = State::new(modules);
let mut checker: Checker<Bn128Field> = new_with_args(HashMap::new(), 0, HashSet::new());
let mut checker: Checker<Bn128Field> = new_with_args(Scope::default(), HashSet::new());
assert_eq!(
checker.check_function(bar, &*MODULE_ID, &state),
Err(vec![ErrorInner {
@ -5032,7 +5069,7 @@ mod tests {
let modules = Modules::new();
let state = State::new(modules);
let mut checker: Checker<Bn128Field> = new_with_args(HashMap::new(), 0, HashSet::new());
let mut checker: Checker<Bn128Field> = new_with_args(Scope::default(), HashSet::new());
assert_eq!(
checker.check_function(bar, &*MODULE_ID, &state),
Err(vec![ErrorInner {
@ -5069,13 +5106,13 @@ mod tests {
let modules = Modules::new();
let state = State::new(modules);
let mut checker: Checker<Bn128Field> = new_with_args(HashMap::new(), 0, HashSet::new());
let mut checker: Checker<Bn128Field> = new_with_args(Scope::default(), HashSet::new());
assert_eq!(
checker
.check_function(f, &*MODULE_ID, &state)
.unwrap_err()[0]
.message,
"Duplicate name in function definition: `a` was previously declared as an argument or a generic constant"
"Duplicate name in function definition: `a` was previously declared as an argument, a generic parameter or a constant"
);
}
@ -5161,7 +5198,7 @@ mod tests {
// field a = 2;
// field a = 2;
//
// should fail
// should succeed
let mut checker: Checker<Bn128Field> = Checker::default();
let _: Result<TypedStatement<Bn128Field>, Vec<ErrorInner>> = checker.check_statement(
@ -5183,13 +5220,7 @@ mod tests {
&*MODULE_ID,
&TypeMap::new(),
);
assert_eq!(
s2_checked,
Err(vec![ErrorInner {
pos: Some((Position::mock(), Position::mock())),
message: "Duplicate declaration for variable named a".into()
}])
);
assert!(s2_checked.is_ok());
}
#[test]
@ -5197,7 +5228,7 @@ mod tests {
// field a = 2;
// bool a = true;
//
// should fail
// should succeed
let mut checker: Checker<Bn128Field> = Checker::default();
let _: Result<TypedStatement<Bn128Field>, Vec<ErrorInner>> = checker.check_statement(
@ -5219,12 +5250,10 @@ mod tests {
&*MODULE_ID,
&TypeMap::new(),
);
assert!(s2_checked.is_ok());
assert_eq!(
s2_checked,
Err(vec![ErrorInner {
pos: Some((Position::mock(), Position::mock())),
message: "Duplicate declaration for variable named a".into()
}])
checker.scope.get(&"a").unwrap().ty,
DeclarationType::Boolean
);
}

View file

@ -1,7 +1,7 @@
use zokrates_ast::typed::{
folder::*, BlockExpression, BooleanExpression, Conditional, ConditionalExpression,
ConditionalOrExpression, CoreIdentifier, Expr, Identifier, Type, TypedProgram, TypedStatement,
Variable,
Variable, ShadowedIdentifier,
};
use zokrates_field::Field;
@ -64,7 +64,7 @@ impl<'ast, T: Field> Folder<'ast, T> for ConditionRedefiner<'ast, T> {
condition @ BooleanExpression::Value(_)
| condition @ BooleanExpression::Identifier(_) => condition,
condition => {
let condition_id = Identifier::from(CoreIdentifier::Condition(self.index));
let condition_id = Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Condition(self.index)));
self.buffer.push(TypedStatement::Definition(
Variable::immutable(condition_id.clone(), Type::Boolean).into(),
condition.into(),

View file

@ -5,7 +5,7 @@ use zokrates_ast::typed::{
folder::*, ArrayExpression, ArrayExpressionInner, ArrayType, BooleanExpression, CoreIdentifier,
DeclarationConstant, Expr, FieldElementExpression, Identifier, StructExpression,
StructExpressionInner, StructType, TupleExpression, TupleExpressionInner, TupleType,
TypedProgram, TypedSymbolDeclaration, UBitwidth, UExpression, UExpressionInner,
TypedProgram, TypedSymbolDeclaration, UBitwidth, UExpression, UExpressionInner, ShadowedIdentifier,
};
use zokrates_field::Field;
@ -59,16 +59,17 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
) -> FieldElementExpression<'ast, T> {
match e {
FieldElementExpression::Identifier(Identifier {
id: ShadowedIdentifier {
id: CoreIdentifier::Constant(c),
shadow
},
version,
}) => {
assert_eq!(version, 0);
assert_eq!(shadow, 0);
match self.constants.get(&c).cloned() {
Some(v) => v.try_into().unwrap(),
None => FieldElementExpression::Identifier(Identifier {
id: CoreIdentifier::Constant(c),
version,
}),
None => FieldElementExpression::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
}
}
e => fold_field_expression(self, e),
@ -81,16 +82,17 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
) -> BooleanExpression<'ast, T> {
match e {
BooleanExpression::Identifier(Identifier {
id: ShadowedIdentifier {
id: CoreIdentifier::Constant(c),
shadow
},
version,
}) => {
assert_eq!(version, 0);
assert_eq!(shadow, 0);
match self.constants.get(&c).cloned() {
Some(v) => v.try_into().unwrap(),
None => BooleanExpression::Identifier(Identifier {
id: CoreIdentifier::Constant(c),
version,
}),
None => BooleanExpression::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
}
}
e => fold_boolean_expression(self, e),
@ -104,17 +106,17 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
) -> UExpressionInner<'ast, T> {
match e {
UExpressionInner::Identifier(Identifier {
id: ShadowedIdentifier {
id: CoreIdentifier::Constant(c),
shadow
},
version,
}) => {
assert_eq!(version, 0);
assert_eq!(shadow, 0);
match self.constants.get(&c).cloned() {
Some(v) => UExpression::try_from(v).unwrap().into_inner(),
None => UExpressionInner::Identifier(Identifier {
id: CoreIdentifier::Constant(c),
version,
}),
None => UExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
}
}
e => fold_uint_expression_inner(self, ty, e),
@ -128,16 +130,17 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
) -> ArrayExpressionInner<'ast, T> {
match e {
ArrayExpressionInner::Identifier(Identifier {
id: ShadowedIdentifier {
id: CoreIdentifier::Constant(c),
shadow
},
version,
}) => {
assert_eq!(version, 0);
assert_eq!(shadow, 0);
match self.constants.get(&c).cloned() {
Some(v) => ArrayExpression::try_from(v).unwrap().into_inner(),
None => ArrayExpressionInner::Identifier(Identifier {
id: CoreIdentifier::Constant(c),
version,
}),
None => ArrayExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
}
}
e => fold_array_expression_inner(self, ty, e),
@ -151,16 +154,17 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
) -> TupleExpressionInner<'ast, T> {
match e {
TupleExpressionInner::Identifier(Identifier {
id: ShadowedIdentifier {
id: CoreIdentifier::Constant(c),
shadow
},
version,
}) => {
assert_eq!(version, 0);
assert_eq!(shadow, 0);
match self.constants.get(&c).cloned() {
Some(v) => TupleExpression::try_from(v).unwrap().into_inner(),
None => TupleExpressionInner::Identifier(Identifier {
id: CoreIdentifier::Constant(c),
version,
}),
None => TupleExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
}
}
e => fold_tuple_expression_inner(self, ty, e),
@ -174,16 +178,17 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
) -> StructExpressionInner<'ast, T> {
match e {
StructExpressionInner::Identifier(Identifier {
id: ShadowedIdentifier {
id: CoreIdentifier::Constant(c),
shadow
},
version,
}) => {
assert_eq!(version, 0);
assert_eq!(shadow, 0);
match self.constants.get(&c).cloned() {
Some(v) => StructExpression::try_from(v).unwrap().into_inner(),
None => StructExpressionInner::Identifier(Identifier {
id: CoreIdentifier::Constant(c),
version,
}),
None => StructExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
}
}
e => fold_struct_expression_inner(self, ty, e),

View file

@ -31,10 +31,10 @@ use crate::static_analysis::reducer::ShallowTransformer;
use crate::static_analysis::reducer::Versions;
use zokrates_ast::common::FlatEmbed;
use zokrates_ast::typed::ShadowedIdentifier;
use zokrates_ast::typed::types::{ConcreteGenericsAssignment, IntoType};
use zokrates_ast::typed::CoreIdentifier;
use zokrates_ast::typed::Identifier;
use zokrates_ast::typed::TypedAssignee;
use zokrates_ast::typed::{
ConcreteFunctionKey, ConcreteSignature, ConcreteVariable, DeclarationFunctionKey, Expr,
Signature, Type, TypedExpression, TypedFunctionSymbol, TypedFunctionSymbolDeclaration,
@ -178,7 +178,7 @@ pub fn inline_call<'a, 'ast, T: Field, E: Expr<'ast, T>>(
.zip(inferred_signature.inputs.clone())
.map(|(p, t)| ConcreteVariable::new(p.id.id, t, false))
.zip(arguments.clone())
.map(|(v, a)| TypedStatement::Definition(TypedAssignee::Identifier(v.into()), a))
.map(|(v, a)| TypedStatement::Definition(Variable::from(v).into(), a))
.collect();
let (statements, mut returns): (Vec<_>, Vec<_>) = ssa_f
@ -194,9 +194,9 @@ pub fn inline_call<'a, 'ast, T: Field, E: Expr<'ast, T>>(
};
let v: ConcreteVariable<'ast> = ConcreteVariable::new(
Identifier::from(CoreIdentifier::Call(0)).version(
Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Call(0))).version(
*versions
.entry(CoreIdentifier::Call(0).clone())
.entry(ShadowedIdentifier::top_level(CoreIdentifier::Call(0)))
.and_modify(|e| *e += 1) // if it was already declared, we increment
.or_insert(0),
),
@ -207,7 +207,7 @@ pub fn inline_call<'a, 'ast, T: Field, E: Expr<'ast, T>>(
let expression = TypedExpression::from(Variable::from(v.clone()));
let output_binding =
TypedStatement::Definition(TypedAssignee::Identifier(v.into()), return_expression);
TypedStatement::Definition(Variable::from(v).into(), return_expression);
let pop_log = TypedStatement::PopCallLog;

View file

@ -18,6 +18,7 @@ mod shallow_ssa;
use self::inline::{inline_call, InlineError};
use std::collections::HashMap;
use zokrates_ast::typed::ShadowedIdentifier;
use zokrates_ast::typed::result_folder::*;
use zokrates_ast::typed::types::ConcreteGenericsAssignment;
use zokrates_ast::typed::types::GGenericsAssignment;
@ -47,7 +48,7 @@ pub type ConstantDefinitions<'ast, T> =
HashMap<CanonicalConstantIdentifier<'ast>, TypedExpression<'ast, T>>;
// An SSA version map, giving access to the latest version number for each identifier
pub type Versions<'ast> = HashMap<CoreIdentifier<'ast>, usize>;
pub type Versions<'ast> = HashMap<ShadowedIdentifier<'ast>, usize>;
// A container to represent whether more treatment must be applied to the function
#[derive(Debug, PartialEq, Eq)]
@ -85,7 +86,7 @@ impl fmt::Display for Error {
}
#[derive(Debug, Default)]
struct Substitutions<'ast>(HashMap<CoreIdentifier<'ast>, HashMap<usize, usize>>);
struct Substitutions<'ast>(HashMap<ShadowedIdentifier<'ast>, HashMap<usize, usize>>);
impl<'ast> Substitutions<'ast> {
// create an equivalent substitution map where all paths
@ -266,10 +267,10 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Reducer<'ast, 'a, T> {
)))
}
Err(InlineError::Flat(embed, generics, arguments, output_type)) => {
let identifier = Identifier::from(CoreIdentifier::Call(0)).version(
let identifier = Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Call(0))).version(
*self
.versions
.entry(CoreIdentifier::Call(0).clone())
.entry(ShadowedIdentifier::top_level(CoreIdentifier::Call(0)))
.and_modify(|e| *e += 1) // if it was already declared, we increment
.or_insert(0),
);

View file

@ -60,7 +60,7 @@ impl<'ast, 'a> ShallowTransformer<'ast, 'a> {
ret
}
fn issue_next_identifier(&mut self, c_id: CoreIdentifier<'ast>) -> Identifier<'ast> {
fn issue_next_identifier(&mut self, c_id: ShadowedIdentifier<'ast>) -> Identifier<'ast> {
let version = *self
.versions
.entry(c_id.clone())
@ -106,11 +106,11 @@ impl<'ast, 'a> ShallowTransformer<'ast, 'a> {
.iter()
.map(|(g, v)| {
TypedStatement::Definition(
TypedAssignee::Identifier(Variable::new(
g.name(),
Variable::new(
ShadowedIdentifier::function_level(g.name().into()),
Type::Uint(UBitwidth::B32),
false,
)),
).into(),
UExpression::from(*v as u32).into(),
)
})
@ -744,6 +744,137 @@ mod tests {
}
}
mod shadowing {
use zokrates_ast::typed::types::GGenericsAssignment;
use super::*;
#[test]
fn same_scope() {
// def main(field a) {
// field a = 42;
// bool a = true
// return;
// }
// should become
// def main(field a_0) {
// field a_1 = 42;
// bool a_2 = true;
// return;
// }
let f: TypedFunction<Bn128Field> = TypedFunction {
arguments: vec![DeclarationVariable::field_element("a").into()],
statements: vec![
TypedStatement::Definition(
Variable::field_element("a").into(),
TypedExpression::Uint(42u32.into()),
),
TypedStatement::Definition(
Variable::boolean("a").into(),
BooleanExpression::Value(true).into(),
),
TypedStatement::Return(TupleExpressionInner::Value(vec![]).annotate(TupleType::new(vec![])).into())
],
signature: DeclarationSignature::new()
.generics(vec![])
.inputs(vec![DeclarationType::FieldElement])
};
let expected: TypedFunction<Bn128Field> = TypedFunction {
arguments: vec![DeclarationVariable::field_element("a").into()],
statements: vec![
TypedStatement::Definition(
Variable::field_element(Identifier::from("a").version(1)).into(),
TypedExpression::Uint(42u32.into()),
),
TypedStatement::Definition(
Variable::boolean(Identifier::from("a").version(2)).into(),
BooleanExpression::Value(true).into(),
),
TypedStatement::Return(TupleExpressionInner::Value(vec![]).annotate(TupleType::new(vec![])).into())
],
signature: DeclarationSignature::new()
.generics(vec![])
.inputs(vec![DeclarationType::FieldElement])
};
let mut versions = Versions::default();
let ssa = ShallowTransformer::transform(
f,
&GGenericsAssignment::default(),
&mut versions,
);
assert_eq!(ssa, Output::Complete(expected));
}
#[test]
fn next_scope() {
// def main(field a) {
// for u32 i in 0..1 {
// a = a + 1
// field a = 42
// }
// return a
// }
// should become
// def main(field a_0) {
// # versions: {a: 0}
// for u32 i in 0..1 {
// a_0 = a_0
// field a_0 = 42
// }
// return a_1
// }
let f: TypedFunction<Bn128Field> = TypedFunction {
arguments: vec![DeclarationVariable::field_element("a").into()],
statements: vec![
TypedStatement::For(Variable::uint("i", UBitwidth::B32), 0u32.into(), 1u32.into(), vec![
TypedStatement::Definition(Variable::field_element(Identifier::from("a")).into(), FieldElementExpression::Identifier("a".into()).into()),
TypedStatement::Definition(Variable::field_element(Identifier::from("a")).into(), FieldElementExpression::Number(42usize.into()).into()),
]),
TypedStatement::Return(TupleExpressionInner::Value(vec![FieldElementExpression::Identifier("a".into()).into()]).annotate(TupleType::new(vec![Type::FieldElement])).into())
],
signature: DeclarationSignature::new()
.generics(vec![])
.inputs(vec![DeclarationType::FieldElement])
.output(DeclarationType::FieldElement)
};
let expected: TypedFunction<Bn128Field> = TypedFunction {
arguments: vec![DeclarationVariable::field_element("a").into()],
statements: vec![
TypedStatement::For(Variable::uint("i", UBitwidth::B32), 0u32.into(), 1u32.into(), vec![
TypedStatement::Definition(Variable::field_element(Identifier::from("a")).into(), FieldElementExpression::Identifier(Identifier::from("a")).into()),
TypedStatement::Definition(Variable::field_element(Identifier::from("a")).into(), FieldElementExpression::Number(42usize.into()).into()),
]),
TypedStatement::Return(TupleExpressionInner::Value(vec![FieldElementExpression::Identifier(Identifier::from("a").version(1)).into()]).annotate(TupleType::new(vec![Type::FieldElement])).into())
],
signature: DeclarationSignature::new()
.generics(vec![])
.inputs(vec![DeclarationType::FieldElement])
.output(DeclarationType::FieldElement)
};
let mut versions = Versions::default();
let ssa = ShallowTransformer::transform(
f,
&GGenericsAssignment::default(),
&mut versions,
);
assert_eq!(ssa, Output::Incomplete(expected, vec![vec![("a".into(), 0)].into_iter().collect()]));
}
}
mod function_call {
use super::*;
use zokrates_ast::typed::types::GGenericsAssignment;