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> {
|
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(c) => {
|
||||||
CoreIdentifier::Constant(self.fold_canonical_constant_identifier(c))
|
CoreIdentifier::Constant(self.fold_canonical_constant_identifier(c))
|
||||||
}
|
}
|
||||||
id => id,
|
id => id,
|
||||||
};
|
};
|
||||||
|
|
||||||
Identifier { id, ..n }
|
Identifier { id: ShadowedIdentifier {id, ..n.id}, ..n }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fold_variable(&mut self, v: Variable<'ast, T>) -> Variable<'ast, T> {
|
fn fold_variable(&mut self, v: Variable<'ast, T>) -> Variable<'ast, T> {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::typed::CanonicalConstantIdentifier;
|
use crate::typed::CanonicalConstantIdentifier;
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
pub type SourceIdentifier<'ast> = &'ast str;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
|
||||||
pub enum CoreIdentifier<'ast> {
|
pub enum CoreIdentifier<'ast> {
|
||||||
Source(&'ast str),
|
Source(&'ast str),
|
||||||
|
@ -36,23 +37,56 @@ impl<'ast> From<CanonicalConstantIdentifier<'ast>> for CoreIdentifier<'ast> {
|
||||||
/// A identifier for a variable
|
/// A identifier for a variable
|
||||||
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
|
||||||
pub struct Identifier<'ast> {
|
pub struct Identifier<'ast> {
|
||||||
/// the id of the variable
|
/// the id of the variable with its shadowing id
|
||||||
pub id: CoreIdentifier<'ast>,
|
pub id: ShadowedIdentifier<'ast>,
|
||||||
/// the version of the variable, used after SSA transformation
|
/// the version of the variable, used after SSA transformation
|
||||||
pub version: usize,
|
pub version: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ast> TryInto<&'ast str> for Identifier<'ast> {
|
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
|
||||||
type Error = ();
|
pub struct ShadowedIdentifier<'ast> {
|
||||||
|
pub id: CoreIdentifier<'ast>,
|
||||||
|
pub shadow: usize,
|
||||||
|
}
|
||||||
|
|
||||||
fn try_into(self) -> Result<&'ast str, Self::Error> {
|
impl<'ast> ShadowedIdentifier<'ast> {
|
||||||
match self.id {
|
pub fn top_level(id: CoreIdentifier<'ast>) -> Self {
|
||||||
CoreIdentifier::Source(i) => Ok(i),
|
Self::nth(id, 0)
|
||||||
_ => Err(()),
|
}
|
||||||
|
|
||||||
|
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> {
|
impl<'ast> fmt::Display for Identifier<'ast> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
if self.version == 0 {
|
if self.version == 0 {
|
||||||
|
@ -65,19 +99,37 @@ impl<'ast> fmt::Display for Identifier<'ast> {
|
||||||
|
|
||||||
impl<'ast> From<CanonicalConstantIdentifier<'ast>> for Identifier<'ast> {
|
impl<'ast> From<CanonicalConstantIdentifier<'ast>> for Identifier<'ast> {
|
||||||
fn from(id: CanonicalConstantIdentifier<'ast>) -> 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> {
|
impl<'ast> From<&'ast str> for Identifier<'ast> {
|
||||||
fn from(id: &'ast str) -> 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> {
|
impl<'ast> From<CoreIdentifier<'ast>> for Identifier<'ast> {
|
||||||
fn from(id: CoreIdentifier<'ast>) -> 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;
|
mod uint;
|
||||||
pub mod variable;
|
pub mod variable;
|
||||||
|
|
||||||
pub use self::identifier::CoreIdentifier;
|
pub use self::identifier::{CoreIdentifier, SourceIdentifier, ShadowedIdentifier};
|
||||||
pub use self::parameter::{DeclarationParameter, GParameter};
|
pub use self::parameter::{DeclarationParameter, GParameter};
|
||||||
pub use self::types::{
|
pub use self::types::{
|
||||||
CanonicalConstantIdentifier, ConcreteFunctionKey, ConcreteSignature, ConcreteTupleType,
|
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> {
|
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(c) => {
|
||||||
CoreIdentifier::Constant(self.fold_canonical_constant_identifier(c)?)
|
CoreIdentifier::Constant(self.fold_canonical_constant_identifier(c)?)
|
||||||
}
|
}
|
||||||
id => id,
|
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> {
|
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 {
|
fn from(c: DeclarationConstant<'ast, T>) -> Self {
|
||||||
match c {
|
match c {
|
||||||
DeclarationConstant::Generic(i) => {
|
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) => {
|
DeclarationConstant::Concrete(v) => {
|
||||||
UExpressionInner::Value(v as u128).annotate(UBitwidth::B32)
|
UExpressionInner::Value(v as u128).annotate(UBitwidth::B32)
|
||||||
}
|
}
|
||||||
DeclarationConstant::Constant(v) => {
|
DeclarationConstant::Constant(v) => {
|
||||||
UExpressionInner::Identifier(CoreIdentifier::from(v).into())
|
UExpressionInner::Identifier(ShadowedIdentifier::top_level(CoreIdentifier::from(v)).into())
|
||||||
.annotate(UBitwidth::B32)
|
.annotate(UBitwidth::B32)
|
||||||
}
|
}
|
||||||
DeclarationConstant::Expression(e) => e.try_into().unwrap(),
|
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> {
|
impl<'ast, T> From<CanonicalConstantIdentifier<'ast>> for UExpression<'ast, T> {
|
||||||
fn from(c: CanonicalConstantIdentifier<'ast>) -> Self {
|
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)
|
.annotate(UBitwidth::B32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1215,6 +1215,8 @@ pub use self::signature::{
|
||||||
try_from_g_signature, ConcreteSignature, DeclarationSignature, GSignature, Signature,
|
try_from_g_signature, ConcreteSignature, DeclarationSignature, GSignature, Signature,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::ShadowedIdentifier;
|
||||||
|
|
||||||
pub mod signature {
|
pub mod signature {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::fmt;
|
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> {
|
// impl<'ast, T: Field> From<&'ast str> for UExpressionInner<'ast, T> {
|
||||||
fn from(e: &'ast str) -> Self {
|
// fn from(e: &'ast str) -> Self {
|
||||||
UExpressionInner::Identifier(e.into())
|
// UExpressionInner::Identifier(e.into())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct UMetadata {
|
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> {
|
impl<'ast, T> From<u32> for UExpression<'ast, T> {
|
||||||
fn from(u: u32) -> Self {
|
fn from(u: u32) -> Self {
|
||||||
UExpressionInner::Value(u as u128).annotate(UBitwidth::B32)
|
UExpressionInner::Value(u as u128).annotate(UBitwidth::B32)
|
||||||
|
|
|
@ -5,11 +5,12 @@
|
||||||
//! @date 2017
|
//! @date 2017
|
||||||
|
|
||||||
use num_bigint::BigUint;
|
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::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use zokrates_ast::common::FormatString;
|
use zokrates_ast::common::FormatString;
|
||||||
use zokrates_ast::typed::types::{GGenericsAssignment, GTupleType, GenericsAssignment};
|
use zokrates_ast::typed::types::{GGenericsAssignment, GTupleType, GenericsAssignment};
|
||||||
|
use zokrates_ast::typed::SourceIdentifier;
|
||||||
use zokrates_ast::typed::*;
|
use zokrates_ast::typed::*;
|
||||||
use zokrates_ast::typed::{DeclarationParameter, DeclarationVariable, Variable};
|
use zokrates_ast::typed::{DeclarationParameter, DeclarationVariable, Variable};
|
||||||
use zokrates_ast::untyped::Identifier;
|
use zokrates_ast::untyped::Identifier;
|
||||||
|
@ -18,7 +19,7 @@ use zokrates_field::Field;
|
||||||
|
|
||||||
use zokrates_ast::untyped::types::{UnresolvedSignature, UnresolvedType, UserTypeId};
|
use zokrates_ast::untyped::types::{UnresolvedSignature, UnresolvedType, UserTypeId};
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::Hash;
|
||||||
use zokrates_ast::typed::types::{
|
use zokrates_ast::typed::types::{
|
||||||
check_type, specialize_declaration_type, ArrayType, DeclarationArrayType, DeclarationConstant,
|
check_type, specialize_declaration_type, ArrayType, DeclarationArrayType, DeclarationConstant,
|
||||||
DeclarationFunctionKey, DeclarationSignature, DeclarationStructMember, DeclarationStructType,
|
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(Debug, Clone)]
|
||||||
#[derive(Clone, Debug)]
|
pub struct IdentifierInfo<'ast, T, U> {
|
||||||
pub struct ScopedIdentifier<'ast> {
|
id: U,
|
||||||
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> {
|
|
||||||
ty: Type<'ast, T>,
|
ty: Type<'ast, T>,
|
||||||
is_mutable: bool,
|
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
|
/// Checker checks the semantics of a program, keeping track of functions and variables in scope
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -328,7 +321,6 @@ pub struct Checker<'ast, T> {
|
||||||
return_type: Option<DeclarationType<'ast, T>>,
|
return_type: Option<DeclarationType<'ast, T>>,
|
||||||
scope: Scope<'ast, T>,
|
scope: Scope<'ast, T>,
|
||||||
functions: HashSet<DeclarationFunctionKey<'ast, T>>,
|
functions: HashSet<DeclarationFunctionKey<'ast, T>>,
|
||||||
level: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ast, T: Field> Checker<'ast, T> {
|
impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
|
@ -693,13 +685,18 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
self.insert_into_scope(Variable::immutable(
|
let id = declaration.id;
|
||||||
CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
|
|
||||||
|
let info = IdentifierInfo {
|
||||||
|
id: CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
|
||||||
declaration.id,
|
declaration.id,
|
||||||
module_id.into(),
|
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
|
assert!(state
|
||||||
.constants
|
.constants
|
||||||
.entry(module_id.to_path_buf())
|
.entry(module_id.to_path_buf())
|
||||||
|
@ -884,10 +881,18 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
id.clone(),
|
id.clone(),
|
||||||
TypedConstantSymbol::There(imported_id)
|
TypedConstantSymbol::There(imported_id)
|
||||||
).into());
|
).into());
|
||||||
self.insert_into_scope(Variable::immutable(CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
|
|
||||||
declaration.id,
|
let id = declaration.id;
|
||||||
module_id.into(),
|
let info = IdentifierInfo {
|
||||||
)), zokrates_ast::typed::types::try_from_g_type(ty.clone()).unwrap()));
|
id: CoreIdentifier::Constant(CanonicalConstantIdentifier::new(
|
||||||
|
declaration.id,
|
||||||
|
module_id.into(),
|
||||||
|
)),
|
||||||
|
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
|
state
|
||||||
.constants
|
.constants
|
||||||
|
@ -1072,8 +1077,6 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
}]),
|
}]),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
self.insert_into_scope(var.clone());
|
|
||||||
|
|
||||||
Ok(var)
|
Ok(var)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1112,17 +1115,14 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// for declaration signatures, generics cannot be ignored
|
// for declaration signatures, generics cannot be ignored
|
||||||
|
|
||||||
let v = Variable::immutable(generic.name(), Type::Uint(UBitwidth::B32));
|
|
||||||
|
|
||||||
generics.0.insert(
|
generics.0.insert(
|
||||||
generic.clone(),
|
generic.clone(),
|
||||||
UExpressionInner::Identifier(generic.name().into())
|
UExpressionInner::Identifier(ShadowedIdentifier::nth(generic.name().into(), self.scope.level).into())
|
||||||
.annotate(UBitwidth::B32),
|
.annotate(UBitwidth::B32),
|
||||||
);
|
);
|
||||||
|
|
||||||
// we don't have to check for conflicts here, because this was done when checking the signature
|
//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()) {
|
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 arg = arg.value;
|
||||||
|
|
||||||
let decl_v = DeclarationVariable::new(
|
let decl_v = DeclarationVariable::new(
|
||||||
arg.id.value.id,
|
ShadowedIdentifier::nth(arg.id.value.id.into(), 1),
|
||||||
decl_ty.clone(),
|
decl_ty.clone(),
|
||||||
arg.id.value.is_mutable,
|
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();
|
let ty = specialize_declaration_type(decl_v.clone()._type, &generics).unwrap();
|
||||||
|
|
||||||
match self.insert_into_scope(zokrates_ast::typed::variable::Variable {
|
assert_eq!(self.scope.level, 1);
|
||||||
id: decl_v.clone().id,
|
|
||||||
_type: ty,
|
let id = arg.id.value.id;
|
||||||
|
let info = IdentifierInfo {
|
||||||
|
id: decl_v.id.id.id.clone(),
|
||||||
|
ty,
|
||||||
is_mutable,
|
is_mutable,
|
||||||
}) {
|
};
|
||||||
true => {}
|
match self.scope.insert(id, info) {
|
||||||
false => {
|
false => {}
|
||||||
|
true => {
|
||||||
errors.push(ErrorInner {
|
errors.push(ErrorInner {
|
||||||
pos: Some(pos),
|
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,
|
module_id: &ModuleId,
|
||||||
types: &TypeMap<'ast, T>,
|
types: &TypeMap<'ast, T>,
|
||||||
) -> Result<Variable<'ast, T>, Vec<ErrorInner>> {
|
) -> 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(
|
Ok(Variable::new(
|
||||||
v.value.id,
|
ShadowedIdentifier::nth(v.value.id.into(), self.scope.level),
|
||||||
self.check_type(v.value._type, module_id, types)
|
ty,
|
||||||
.map_err(|e| vec![e])?,
|
|
||||||
v.value.is_mutable,
|
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
|
// the assignee is already checked to be defined and mutable
|
||||||
fn check_assignment_inner(
|
fn check_rhs(
|
||||||
&mut self,
|
&mut self,
|
||||||
assignee: TypedAssignee<'ast, T>,
|
return_type: Type<'ast, T>,
|
||||||
expr: ExpressionNode<'ast>,
|
expr: ExpressionNode<'ast>,
|
||||||
statement_pos: (Position, Position),
|
|
||||||
module_id: &ModuleId,
|
module_id: &ModuleId,
|
||||||
types: &TypeMap<'ast, T>,
|
types: &TypeMap<'ast, T>,
|
||||||
) -> Result<TypedStatement<'ast, T>, Vec<ErrorInner>> {
|
) -> Result<TypedExpression<'ast, T>, ErrorInner> {
|
||||||
let assignee_type = assignee.get_type();
|
|
||||||
|
|
||||||
match expr.value {
|
match expr.value {
|
||||||
Expression::FunctionCall(box fun_id_expression, generics, arguments) => {
|
Expression::FunctionCall(box fun_id_expression, generics, arguments) => {
|
||||||
let e = self
|
self
|
||||||
.check_function_call_expression(
|
.check_function_call_expression(
|
||||||
fun_id_expression,
|
fun_id_expression,
|
||||||
generics,
|
generics,
|
||||||
arguments,
|
arguments,
|
||||||
Some(assignee_type),
|
Some(return_type),
|
||||||
module_id,
|
module_id,
|
||||||
types,
|
types,
|
||||||
)
|
)
|
||||||
.map_err(|e| vec![e])?;
|
|
||||||
Ok(TypedStatement::Definition(assignee, e))
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// check the expression to be assigned
|
// check the expression to be assigned
|
||||||
let checked_expr = self
|
self
|
||||||
.check_expression(expr, module_id, types)
|
.check_expression(expr, module_id, types)
|
||||||
.map_err(|e| vec![e])?;
|
|
||||||
|
|
||||||
// make sure the assignee has the same type as the rhs
|
// // make sure the assignee has the same type as the rhs
|
||||||
match assignee_type {
|
// match return_type {
|
||||||
Type::FieldElement => FieldElementExpression::try_from_typed(checked_expr)
|
// Type::FieldElement => FieldElementExpression::try_from_typed(checked_expr)
|
||||||
.map(TypedExpression::from),
|
// .map(TypedExpression::from),
|
||||||
Type::Boolean => {
|
// Type::Boolean => {
|
||||||
BooleanExpression::try_from_typed(checked_expr).map(TypedExpression::from)
|
// BooleanExpression::try_from_typed(checked_expr).map(TypedExpression::from)
|
||||||
}
|
// }
|
||||||
Type::Uint(bitwidth) => UExpression::try_from_typed(checked_expr, &bitwidth)
|
// Type::Uint(bitwidth) => UExpression::try_from_typed(checked_expr, &bitwidth)
|
||||||
.map(TypedExpression::from),
|
// .map(TypedExpression::from),
|
||||||
Type::Array(ref array_ty) => {
|
// Type::Array(ref array_ty) => {
|
||||||
ArrayExpression::try_from_typed(checked_expr, array_ty)
|
// ArrayExpression::try_from_typed(checked_expr, array_ty)
|
||||||
.map(TypedExpression::from)
|
// .map(TypedExpression::from)
|
||||||
}
|
// }
|
||||||
Type::Struct(ref struct_ty) => {
|
// Type::Struct(ref struct_ty) => {
|
||||||
StructExpression::try_from_typed(checked_expr, struct_ty)
|
// StructExpression::try_from_typed(checked_expr, struct_ty)
|
||||||
.map(TypedExpression::from)
|
// .map(TypedExpression::from)
|
||||||
}
|
// }
|
||||||
Type::Tuple(ref tuple_ty) => {
|
// Type::Tuple(ref tuple_ty) => {
|
||||||
TupleExpression::try_from_typed(checked_expr, tuple_ty)
|
// TupleExpression::try_from_typed(checked_expr, tuple_ty)
|
||||||
.map(TypedExpression::from)
|
// .map(TypedExpression::from)
|
||||||
}
|
// }
|
||||||
Type::Int => Err(checked_expr), // Integers cannot be assigned
|
// Type::Int => Err(checked_expr), // Integers cannot be assigned
|
||||||
}
|
// }
|
||||||
.map_err(|e| ErrorInner {
|
// .map_err(|e| vec![e])
|
||||||
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])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1896,27 +1889,102 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
Statement::Definition(var, expr) => {
|
Statement::Definition(var, expr) => {
|
||||||
let var = self.check_variable(var, module_id, types)?;
|
|
||||||
match self.insert_into_scope(var.clone()) {
|
// get the lhs type
|
||||||
true => Ok(()),
|
let var_ty = self.check_type(var.value._type, module_id, types)
|
||||||
false => Err(ErrorInner {
|
.map_err(|e| vec![e])?;
|
||||||
pos: Some(pos),
|
|
||||||
message: format!("Duplicate declaration for variable named {}", var.id),
|
// 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])?;
|
||||||
|
|
||||||
|
// 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| vec![e])?;
|
.map_err(|e| ErrorInner {
|
||||||
|
pos: Some(pos),
|
||||||
let assignee = TypedAssignee::Identifier(var);
|
message: format!(
|
||||||
|
"Expression `{}` of type `{}` cannot be assigned to `{}` of type `{}`",
|
||||||
self.check_assignment_inner(assignee, expr, pos, module_id, types)
|
e,
|
||||||
|
e.get_type(),
|
||||||
|
var.clone(),
|
||||||
|
var_ty
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.map(|e| TypedStatement::Definition(var.into(), e))
|
||||||
|
.map_err(|e| vec![e])
|
||||||
}
|
}
|
||||||
Statement::Assignment(assignee, expr) => {
|
Statement::Assignment(assignee, expr) => {
|
||||||
// check that the assignee is declared, well formed and mutable
|
// check that the assignee is declared, well formed and mutable
|
||||||
let var = self
|
let assignee = self
|
||||||
.check_assignee(assignee, module_id, types)
|
.check_assignee(assignee, module_id, types)
|
||||||
.map_err(|e| vec![e])?;
|
.map_err(|e| vec![e])?;
|
||||||
|
|
||||||
|
let assignee_ty = assignee.get_type();
|
||||||
|
|
||||||
self.check_assignment_inner(var, expr, pos, module_id, types)
|
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) => {
|
Statement::Assertion(e, message) => {
|
||||||
let e = self
|
let e = self
|
||||||
|
@ -1964,18 +2032,14 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
let pos = assignee.pos();
|
let pos = assignee.pos();
|
||||||
// check that the assignee is declared
|
// check that the assignee is declared
|
||||||
match assignee.value {
|
match assignee.value {
|
||||||
Assignee::Identifier(variable_name) => match self.get_key_value_scope(variable_name) {
|
Assignee::Identifier(variable_name) => match self.scope.get(&variable_name) {
|
||||||
Some((id, info)) => match (id.is_constant(), info.is_mutable) {
|
Some(info) => match info.is_mutable {
|
||||||
(true, _) => Err(ErrorInner {
|
false => Err(ErrorInner {
|
||||||
pos: Some(assignee.pos()),
|
|
||||||
message: format!("Assignment to constant variable `{}`", variable_name),
|
|
||||||
}),
|
|
||||||
(_, false) => Err(ErrorInner {
|
|
||||||
pos: Some(assignee.pos()),
|
pos: Some(assignee.pos()),
|
||||||
message: format!("Assignment to an immutable variable `{}`", variable_name),
|
message: format!("Assignment to an immutable variable `{}`", variable_name),
|
||||||
}),
|
}),
|
||||||
_ => Ok(TypedAssignee::Identifier(Variable::new(
|
_ => Ok(TypedAssignee::Identifier(Variable::new(
|
||||||
variable_name,
|
info.id,
|
||||||
info.ty.clone(),
|
info.ty.clone(),
|
||||||
info.is_mutable,
|
info.is_mutable,
|
||||||
))),
|
))),
|
||||||
|
@ -2277,32 +2341,34 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
Expression::BooleanConstant(b) => Ok(BooleanExpression::Value(b).into()),
|
Expression::BooleanConstant(b) => Ok(BooleanExpression::Value(b).into()),
|
||||||
Expression::Identifier(name) => {
|
Expression::Identifier(name) => {
|
||||||
// check that `id` is defined in the scope
|
// check that `id` is defined in the scope
|
||||||
match self
|
match self.scope.get(&name) {
|
||||||
.get_key_value_scope(name)
|
Some(info) => {
|
||||||
.map(|(x, y)| (x.clone(), y.ty.clone()))
|
let id = info.id;
|
||||||
{
|
match info.ty.clone() {
|
||||||
Some((id, ty)) => match ty {
|
Type::Boolean => Ok(BooleanExpression::Identifier(id.into()).into()),
|
||||||
Type::Boolean => Ok(BooleanExpression::Identifier(id.id.into()).into()),
|
Type::Uint(bitwidth) => Ok(UExpressionInner::Identifier(id.into())
|
||||||
Type::Uint(bitwidth) => Ok(UExpressionInner::Identifier(id.id.into())
|
.annotate(bitwidth)
|
||||||
.annotate(bitwidth)
|
.into()),
|
||||||
.into()),
|
Type::FieldElement => {
|
||||||
Type::FieldElement => {
|
Ok(FieldElementExpression::Identifier(id.into()).into())
|
||||||
Ok(FieldElementExpression::Identifier(id.id.into()).into())
|
}
|
||||||
|
Type::Array(array_type) => {
|
||||||
|
Ok(ArrayExpressionInner::Identifier(id.into())
|
||||||
|
.annotate(*array_type.ty, *array_type.size)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
Type::Struct(members) => {
|
||||||
|
Ok(StructExpressionInner::Identifier(id.into())
|
||||||
|
.annotate(members)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
Type::Tuple(tuple_ty) => {
|
||||||
|
Ok(TupleExpressionInner::Identifier(id.into())
|
||||||
|
.annotate(tuple_ty)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
Type::Int => unreachable!(),
|
||||||
}
|
}
|
||||||
Type::Array(array_type) => {
|
|
||||||
Ok(ArrayExpressionInner::Identifier(id.id.into())
|
|
||||||
.annotate(*array_type.ty, *array_type.size)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
Type::Struct(members) => {
|
|
||||||
Ok(StructExpressionInner::Identifier(id.id.into())
|
|
||||||
.annotate(members)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
Type::Tuple(tuple_ty) => Ok(TupleExpressionInner::Identifier(id.id.into())
|
|
||||||
.annotate(tuple_ty)
|
|
||||||
.into()),
|
|
||||||
Type::Int => unreachable!(),
|
|
||||||
},
|
},
|
||||||
None => Err(ErrorInner {
|
None => Err(ErrorInner {
|
||||||
pos: Some(pos),
|
pos: Some(pos),
|
||||||
|
@ -3537,36 +3603,13 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_key_value_scope<'a>(
|
fn insert_into_scope(&mut self, id: SourceIdentifier<'ast>, ty: Type<'ast, T>, is_mutable: bool) -> bool {
|
||||||
&'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,
|
|
||||||
};
|
|
||||||
let info = IdentifierInfo {
|
let info = IdentifierInfo {
|
||||||
ty: v._type,
|
id: CoreIdentifier::Source(id),
|
||||||
is_mutable: v.is_mutable,
|
ty,
|
||||||
|
is_mutable,
|
||||||
};
|
};
|
||||||
match self.scope.entry(id) {
|
self.scope.insert(id, info)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_functions(
|
fn find_functions(
|
||||||
|
@ -3577,14 +3620,11 @@ impl<'ast, T: Field> Checker<'ast, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_scope(&mut self) {
|
fn enter_scope(&mut self) {
|
||||||
self.level += 1;
|
self.scope.enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_scope(&mut self) {
|
fn exit_scope(&mut self) {
|
||||||
let current_level = self.level;
|
self.scope.exit();
|
||||||
self.scope
|
|
||||||
.retain(|scoped_variable, _| scoped_variable.level < current_level);
|
|
||||||
self.level -= 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>,
|
scope: Scope<'ast, T>,
|
||||||
level: usize,
|
|
||||||
functions: HashSet<DeclarationFunctionKey<'ast, T>>,
|
functions: HashSet<DeclarationFunctionKey<'ast, T>>,
|
||||||
) -> Checker<'ast, T> {
|
) -> Checker<'ast, T> {
|
||||||
Checker {
|
Checker {
|
||||||
scope,
|
scope,
|
||||||
functions,
|
functions,
|
||||||
level,
|
|
||||||
return_type: None,
|
return_type: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4425,21 +4463,20 @@ mod tests {
|
||||||
|
|
||||||
let mut scope = Scope::default();
|
let mut scope = Scope::default();
|
||||||
scope.insert(
|
scope.insert(
|
||||||
ScopedIdentifier {
|
"b",
|
||||||
id: CoreIdentifier::Source("b"),
|
|
||||||
level: 1,
|
|
||||||
},
|
|
||||||
IdentifierInfo {
|
IdentifierInfo {
|
||||||
|
id: CoreIdentifier::Source("b"),
|
||||||
ty: Type::FieldElement,
|
ty: Type::FieldElement,
|
||||||
is_mutable: false,
|
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!(
|
assert_eq!(
|
||||||
checker.check_statement(statement, &*MODULE_ID, &TypeMap::new()),
|
checker.check_statement(statement, &*MODULE_ID, &TypeMap::new()),
|
||||||
Ok(TypedStatement::Definition(
|
Ok(TypedStatement::Definition(
|
||||||
TypedAssignee::Identifier(typed::Variable::field_element("a")),
|
typed::Variable::field_element("a").into(),
|
||||||
FieldElementExpression::Identifier("b".into()).into()
|
FieldElementExpression::Identifier("b".into()).into()
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
@ -4668,7 +4705,7 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let for_statements_checked = vec![TypedStatement::Definition(
|
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())
|
UExpressionInner::Identifier("i".into())
|
||||||
.annotate(UBitwidth::B32)
|
.annotate(UBitwidth::B32)
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -4751,7 +4788,7 @@ mod tests {
|
||||||
let modules = Modules::new();
|
let modules = Modules::new();
|
||||||
let state = State::new(modules);
|
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!(
|
assert_eq!(
|
||||||
checker.check_function(bar, &*MODULE_ID, &state),
|
checker.check_function(bar, &*MODULE_ID, &state),
|
||||||
Err(vec![ErrorInner {
|
Err(vec![ErrorInner {
|
||||||
|
@ -4790,7 +4827,7 @@ mod tests {
|
||||||
let modules = Modules::new();
|
let modules = Modules::new();
|
||||||
let state = State::new(modules);
|
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!(
|
assert_eq!(
|
||||||
checker.check_function(bar, &*MODULE_ID, &state),
|
checker.check_function(bar, &*MODULE_ID, &state),
|
||||||
Err(vec![ErrorInner {
|
Err(vec![ErrorInner {
|
||||||
|
@ -4863,7 +4900,7 @@ mod tests {
|
||||||
let mut state =
|
let mut state =
|
||||||
State::<Bn128Field>::new(vec![((*MODULE_ID).clone(), module)].into_iter().collect());
|
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!(
|
assert_eq!(
|
||||||
checker.check_module(&*MODULE_ID, &mut state),
|
checker.check_module(&*MODULE_ID, &mut state),
|
||||||
Err(vec![Error {
|
Err(vec![Error {
|
||||||
|
@ -4959,7 +4996,7 @@ mod tests {
|
||||||
let mut state =
|
let mut state =
|
||||||
State::<Bn128Field>::new(vec![((*MODULE_ID).clone(), module)].into_iter().collect());
|
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());
|
assert!(checker.check_module(&*MODULE_ID, &mut state).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4999,7 +5036,7 @@ mod tests {
|
||||||
let modules = Modules::new();
|
let modules = Modules::new();
|
||||||
let state = State::new(modules);
|
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!(
|
assert_eq!(
|
||||||
checker.check_function(bar, &*MODULE_ID, &state),
|
checker.check_function(bar, &*MODULE_ID, &state),
|
||||||
Err(vec![ErrorInner {
|
Err(vec![ErrorInner {
|
||||||
|
@ -5032,7 +5069,7 @@ mod tests {
|
||||||
let modules = Modules::new();
|
let modules = Modules::new();
|
||||||
let state = State::new(modules);
|
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!(
|
assert_eq!(
|
||||||
checker.check_function(bar, &*MODULE_ID, &state),
|
checker.check_function(bar, &*MODULE_ID, &state),
|
||||||
Err(vec![ErrorInner {
|
Err(vec![ErrorInner {
|
||||||
|
@ -5069,13 +5106,13 @@ mod tests {
|
||||||
let modules = Modules::new();
|
let modules = Modules::new();
|
||||||
let state = State::new(modules);
|
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!(
|
assert_eq!(
|
||||||
checker
|
checker
|
||||||
.check_function(f, &*MODULE_ID, &state)
|
.check_function(f, &*MODULE_ID, &state)
|
||||||
.unwrap_err()[0]
|
.unwrap_err()[0]
|
||||||
.message,
|
.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;
|
||||||
// field a = 2;
|
// field a = 2;
|
||||||
//
|
//
|
||||||
// should fail
|
// should succeed
|
||||||
|
|
||||||
let mut checker: Checker<Bn128Field> = Checker::default();
|
let mut checker: Checker<Bn128Field> = Checker::default();
|
||||||
let _: Result<TypedStatement<Bn128Field>, Vec<ErrorInner>> = checker.check_statement(
|
let _: Result<TypedStatement<Bn128Field>, Vec<ErrorInner>> = checker.check_statement(
|
||||||
|
@ -5183,13 +5220,7 @@ mod tests {
|
||||||
&*MODULE_ID,
|
&*MODULE_ID,
|
||||||
&TypeMap::new(),
|
&TypeMap::new(),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert!(s2_checked.is_ok());
|
||||||
s2_checked,
|
|
||||||
Err(vec![ErrorInner {
|
|
||||||
pos: Some((Position::mock(), Position::mock())),
|
|
||||||
message: "Duplicate declaration for variable named a".into()
|
|
||||||
}])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -5197,7 +5228,7 @@ mod tests {
|
||||||
// field a = 2;
|
// field a = 2;
|
||||||
// bool a = true;
|
// bool a = true;
|
||||||
//
|
//
|
||||||
// should fail
|
// should succeed
|
||||||
|
|
||||||
let mut checker: Checker<Bn128Field> = Checker::default();
|
let mut checker: Checker<Bn128Field> = Checker::default();
|
||||||
let _: Result<TypedStatement<Bn128Field>, Vec<ErrorInner>> = checker.check_statement(
|
let _: Result<TypedStatement<Bn128Field>, Vec<ErrorInner>> = checker.check_statement(
|
||||||
|
@ -5219,12 +5250,10 @@ mod tests {
|
||||||
&*MODULE_ID,
|
&*MODULE_ID,
|
||||||
&TypeMap::new(),
|
&TypeMap::new(),
|
||||||
);
|
);
|
||||||
|
assert!(s2_checked.is_ok());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
s2_checked,
|
checker.scope.get(&"a").unwrap().ty,
|
||||||
Err(vec![ErrorInner {
|
DeclarationType::Boolean
|
||||||
pos: Some((Position::mock(), Position::mock())),
|
|
||||||
message: "Duplicate declaration for variable named a".into()
|
|
||||||
}])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use zokrates_ast::typed::{
|
use zokrates_ast::typed::{
|
||||||
folder::*, BlockExpression, BooleanExpression, Conditional, ConditionalExpression,
|
folder::*, BlockExpression, BooleanExpression, Conditional, ConditionalExpression,
|
||||||
ConditionalOrExpression, CoreIdentifier, Expr, Identifier, Type, TypedProgram, TypedStatement,
|
ConditionalOrExpression, CoreIdentifier, Expr, Identifier, Type, TypedProgram, TypedStatement,
|
||||||
Variable,
|
Variable, ShadowedIdentifier,
|
||||||
};
|
};
|
||||||
use zokrates_field::Field;
|
use zokrates_field::Field;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ impl<'ast, T: Field> Folder<'ast, T> for ConditionRedefiner<'ast, T> {
|
||||||
condition @ BooleanExpression::Value(_)
|
condition @ BooleanExpression::Value(_)
|
||||||
| condition @ BooleanExpression::Identifier(_) => condition,
|
| condition @ BooleanExpression::Identifier(_) => condition,
|
||||||
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(
|
self.buffer.push(TypedStatement::Definition(
|
||||||
Variable::immutable(condition_id.clone(), Type::Boolean).into(),
|
Variable::immutable(condition_id.clone(), Type::Boolean).into(),
|
||||||
condition.into(),
|
condition.into(),
|
||||||
|
|
|
@ -5,7 +5,7 @@ use zokrates_ast::typed::{
|
||||||
folder::*, ArrayExpression, ArrayExpressionInner, ArrayType, BooleanExpression, CoreIdentifier,
|
folder::*, ArrayExpression, ArrayExpressionInner, ArrayType, BooleanExpression, CoreIdentifier,
|
||||||
DeclarationConstant, Expr, FieldElementExpression, Identifier, StructExpression,
|
DeclarationConstant, Expr, FieldElementExpression, Identifier, StructExpression,
|
||||||
StructExpressionInner, StructType, TupleExpression, TupleExpressionInner, TupleType,
|
StructExpressionInner, StructType, TupleExpression, TupleExpressionInner, TupleType,
|
||||||
TypedProgram, TypedSymbolDeclaration, UBitwidth, UExpression, UExpressionInner,
|
TypedProgram, TypedSymbolDeclaration, UBitwidth, UExpression, UExpressionInner, ShadowedIdentifier,
|
||||||
};
|
};
|
||||||
use zokrates_field::Field;
|
use zokrates_field::Field;
|
||||||
|
|
||||||
|
@ -59,16 +59,17 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
|
||||||
) -> FieldElementExpression<'ast, T> {
|
) -> FieldElementExpression<'ast, T> {
|
||||||
match e {
|
match e {
|
||||||
FieldElementExpression::Identifier(Identifier {
|
FieldElementExpression::Identifier(Identifier {
|
||||||
id: CoreIdentifier::Constant(c),
|
id: ShadowedIdentifier {
|
||||||
|
id: CoreIdentifier::Constant(c),
|
||||||
|
shadow
|
||||||
|
},
|
||||||
version,
|
version,
|
||||||
}) => {
|
}) => {
|
||||||
assert_eq!(version, 0);
|
assert_eq!(version, 0);
|
||||||
|
assert_eq!(shadow, 0);
|
||||||
match self.constants.get(&c).cloned() {
|
match self.constants.get(&c).cloned() {
|
||||||
Some(v) => v.try_into().unwrap(),
|
Some(v) => v.try_into().unwrap(),
|
||||||
None => FieldElementExpression::Identifier(Identifier {
|
None => FieldElementExpression::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
|
||||||
id: CoreIdentifier::Constant(c),
|
|
||||||
version,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => fold_field_expression(self, e),
|
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> {
|
) -> BooleanExpression<'ast, T> {
|
||||||
match e {
|
match e {
|
||||||
BooleanExpression::Identifier(Identifier {
|
BooleanExpression::Identifier(Identifier {
|
||||||
id: CoreIdentifier::Constant(c),
|
id: ShadowedIdentifier {
|
||||||
|
id: CoreIdentifier::Constant(c),
|
||||||
|
shadow
|
||||||
|
},
|
||||||
version,
|
version,
|
||||||
}) => {
|
}) => {
|
||||||
assert_eq!(version, 0);
|
assert_eq!(version, 0);
|
||||||
|
assert_eq!(shadow, 0);
|
||||||
match self.constants.get(&c).cloned() {
|
match self.constants.get(&c).cloned() {
|
||||||
Some(v) => v.try_into().unwrap(),
|
Some(v) => v.try_into().unwrap(),
|
||||||
None => BooleanExpression::Identifier(Identifier {
|
None => BooleanExpression::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
|
||||||
id: CoreIdentifier::Constant(c),
|
|
||||||
version,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => fold_boolean_expression(self, e),
|
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> {
|
) -> UExpressionInner<'ast, T> {
|
||||||
match e {
|
match e {
|
||||||
UExpressionInner::Identifier(Identifier {
|
UExpressionInner::Identifier(Identifier {
|
||||||
id: CoreIdentifier::Constant(c),
|
id: ShadowedIdentifier {
|
||||||
|
id: CoreIdentifier::Constant(c),
|
||||||
|
shadow
|
||||||
|
},
|
||||||
version,
|
version,
|
||||||
}) => {
|
}) => {
|
||||||
assert_eq!(version, 0);
|
assert_eq!(version, 0);
|
||||||
|
assert_eq!(shadow, 0);
|
||||||
match self.constants.get(&c).cloned() {
|
match self.constants.get(&c).cloned() {
|
||||||
Some(v) => UExpression::try_from(v).unwrap().into_inner(),
|
Some(v) => UExpression::try_from(v).unwrap().into_inner(),
|
||||||
None => UExpressionInner::Identifier(Identifier {
|
None => UExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
|
||||||
id: CoreIdentifier::Constant(c),
|
|
||||||
version,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => fold_uint_expression_inner(self, ty, e),
|
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> {
|
) -> ArrayExpressionInner<'ast, T> {
|
||||||
match e {
|
match e {
|
||||||
ArrayExpressionInner::Identifier(Identifier {
|
ArrayExpressionInner::Identifier(Identifier {
|
||||||
id: CoreIdentifier::Constant(c),
|
id: ShadowedIdentifier {
|
||||||
|
id: CoreIdentifier::Constant(c),
|
||||||
|
shadow
|
||||||
|
},
|
||||||
version,
|
version,
|
||||||
}) => {
|
}) => {
|
||||||
assert_eq!(version, 0);
|
assert_eq!(version, 0);
|
||||||
|
assert_eq!(shadow, 0);
|
||||||
match self.constants.get(&c).cloned() {
|
match self.constants.get(&c).cloned() {
|
||||||
Some(v) => ArrayExpression::try_from(v).unwrap().into_inner(),
|
Some(v) => ArrayExpression::try_from(v).unwrap().into_inner(),
|
||||||
None => ArrayExpressionInner::Identifier(Identifier {
|
None => ArrayExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
|
||||||
id: CoreIdentifier::Constant(c),
|
|
||||||
version,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => fold_array_expression_inner(self, ty, e),
|
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> {
|
) -> TupleExpressionInner<'ast, T> {
|
||||||
match e {
|
match e {
|
||||||
TupleExpressionInner::Identifier(Identifier {
|
TupleExpressionInner::Identifier(Identifier {
|
||||||
id: CoreIdentifier::Constant(c),
|
id: ShadowedIdentifier {
|
||||||
|
id: CoreIdentifier::Constant(c),
|
||||||
|
shadow
|
||||||
|
},
|
||||||
version,
|
version,
|
||||||
}) => {
|
}) => {
|
||||||
assert_eq!(version, 0);
|
assert_eq!(version, 0);
|
||||||
|
assert_eq!(shadow, 0);
|
||||||
match self.constants.get(&c).cloned() {
|
match self.constants.get(&c).cloned() {
|
||||||
Some(v) => TupleExpression::try_from(v).unwrap().into_inner(),
|
Some(v) => TupleExpression::try_from(v).unwrap().into_inner(),
|
||||||
None => TupleExpressionInner::Identifier(Identifier {
|
None => TupleExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
|
||||||
id: CoreIdentifier::Constant(c),
|
|
||||||
version,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => fold_tuple_expression_inner(self, ty, e),
|
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> {
|
) -> StructExpressionInner<'ast, T> {
|
||||||
match e {
|
match e {
|
||||||
StructExpressionInner::Identifier(Identifier {
|
StructExpressionInner::Identifier(Identifier {
|
||||||
id: CoreIdentifier::Constant(c),
|
id: ShadowedIdentifier {
|
||||||
|
id: CoreIdentifier::Constant(c),
|
||||||
|
shadow
|
||||||
|
},
|
||||||
version,
|
version,
|
||||||
}) => {
|
}) => {
|
||||||
assert_eq!(version, 0);
|
assert_eq!(version, 0);
|
||||||
|
assert_eq!(shadow, 0);
|
||||||
match self.constants.get(&c).cloned() {
|
match self.constants.get(&c).cloned() {
|
||||||
Some(v) => StructExpression::try_from(v).unwrap().into_inner(),
|
Some(v) => StructExpression::try_from(v).unwrap().into_inner(),
|
||||||
None => StructExpressionInner::Identifier(Identifier {
|
None => StructExpressionInner::Identifier(Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Constant(c)))),
|
||||||
id: CoreIdentifier::Constant(c),
|
|
||||||
version,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => fold_struct_expression_inner(self, ty, e),
|
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 crate::static_analysis::reducer::Versions;
|
||||||
|
|
||||||
use zokrates_ast::common::FlatEmbed;
|
use zokrates_ast::common::FlatEmbed;
|
||||||
|
use zokrates_ast::typed::ShadowedIdentifier;
|
||||||
use zokrates_ast::typed::types::{ConcreteGenericsAssignment, IntoType};
|
use zokrates_ast::typed::types::{ConcreteGenericsAssignment, IntoType};
|
||||||
use zokrates_ast::typed::CoreIdentifier;
|
use zokrates_ast::typed::CoreIdentifier;
|
||||||
use zokrates_ast::typed::Identifier;
|
use zokrates_ast::typed::Identifier;
|
||||||
use zokrates_ast::typed::TypedAssignee;
|
|
||||||
use zokrates_ast::typed::{
|
use zokrates_ast::typed::{
|
||||||
ConcreteFunctionKey, ConcreteSignature, ConcreteVariable, DeclarationFunctionKey, Expr,
|
ConcreteFunctionKey, ConcreteSignature, ConcreteVariable, DeclarationFunctionKey, Expr,
|
||||||
Signature, Type, TypedExpression, TypedFunctionSymbol, TypedFunctionSymbolDeclaration,
|
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())
|
.zip(inferred_signature.inputs.clone())
|
||||||
.map(|(p, t)| ConcreteVariable::new(p.id.id, t, false))
|
.map(|(p, t)| ConcreteVariable::new(p.id.id, t, false))
|
||||||
.zip(arguments.clone())
|
.zip(arguments.clone())
|
||||||
.map(|(v, a)| TypedStatement::Definition(TypedAssignee::Identifier(v.into()), a))
|
.map(|(v, a)| TypedStatement::Definition(Variable::from(v).into(), a))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let (statements, mut returns): (Vec<_>, Vec<_>) = ssa_f
|
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(
|
let v: ConcreteVariable<'ast> = ConcreteVariable::new(
|
||||||
Identifier::from(CoreIdentifier::Call(0)).version(
|
Identifier::from(ShadowedIdentifier::top_level(CoreIdentifier::Call(0))).version(
|
||||||
*versions
|
*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
|
.and_modify(|e| *e += 1) // if it was already declared, we increment
|
||||||
.or_insert(0),
|
.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 expression = TypedExpression::from(Variable::from(v.clone()));
|
||||||
|
|
||||||
let output_binding =
|
let output_binding =
|
||||||
TypedStatement::Definition(TypedAssignee::Identifier(v.into()), return_expression);
|
TypedStatement::Definition(Variable::from(v).into(), return_expression);
|
||||||
|
|
||||||
let pop_log = TypedStatement::PopCallLog;
|
let pop_log = TypedStatement::PopCallLog;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ mod shallow_ssa;
|
||||||
|
|
||||||
use self::inline::{inline_call, InlineError};
|
use self::inline::{inline_call, InlineError};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use zokrates_ast::typed::ShadowedIdentifier;
|
||||||
use zokrates_ast::typed::result_folder::*;
|
use zokrates_ast::typed::result_folder::*;
|
||||||
use zokrates_ast::typed::types::ConcreteGenericsAssignment;
|
use zokrates_ast::typed::types::ConcreteGenericsAssignment;
|
||||||
use zokrates_ast::typed::types::GGenericsAssignment;
|
use zokrates_ast::typed::types::GGenericsAssignment;
|
||||||
|
@ -47,7 +48,7 @@ pub type ConstantDefinitions<'ast, T> =
|
||||||
HashMap<CanonicalConstantIdentifier<'ast>, TypedExpression<'ast, T>>;
|
HashMap<CanonicalConstantIdentifier<'ast>, TypedExpression<'ast, T>>;
|
||||||
|
|
||||||
// An SSA version map, giving access to the latest version number for each identifier
|
// 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
|
// A container to represent whether more treatment must be applied to the function
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -85,7 +86,7 @@ impl fmt::Display for Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[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> {
|
impl<'ast> Substitutions<'ast> {
|
||||||
// create an equivalent substitution map where all paths
|
// 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)) => {
|
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
|
*self
|
||||||
.versions
|
.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
|
.and_modify(|e| *e += 1) // if it was already declared, we increment
|
||||||
.or_insert(0),
|
.or_insert(0),
|
||||||
);
|
);
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl<'ast, 'a> ShallowTransformer<'ast, 'a> {
|
||||||
ret
|
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
|
let version = *self
|
||||||
.versions
|
.versions
|
||||||
.entry(c_id.clone())
|
.entry(c_id.clone())
|
||||||
|
@ -106,11 +106,11 @@ impl<'ast, 'a> ShallowTransformer<'ast, 'a> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(g, v)| {
|
.map(|(g, v)| {
|
||||||
TypedStatement::Definition(
|
TypedStatement::Definition(
|
||||||
TypedAssignee::Identifier(Variable::new(
|
Variable::new(
|
||||||
g.name(),
|
ShadowedIdentifier::function_level(g.name().into()),
|
||||||
Type::Uint(UBitwidth::B32),
|
Type::Uint(UBitwidth::B32),
|
||||||
false,
|
false,
|
||||||
)),
|
).into(),
|
||||||
UExpression::from(*v as u32).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 {
|
mod function_call {
|
||||||
use super::*;
|
use super::*;
|
||||||
use zokrates_ast::typed::types::GGenericsAssignment;
|
use zokrates_ast::typed::types::GGenericsAssignment;
|
||||||
|
|
Loading…
Reference in a new issue