implement shadowing only at the semantic phase
This commit is contained in:
parent
8f7b124123
commit
b2cf1012e8
14 changed files with 536 additions and 322 deletions
|
@ -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> {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
//we don't have to check for conflicts here, because this was done when checking the signature
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue