From b2cf1012e8310b4fd83a90ec8ca1b6d624e3b34a Mon Sep 17 00:00:00 2001 From: schaeff Date: Thu, 21 Jul 2022 17:52:54 +0200 Subject: [PATCH] implement shadowing only at the semantic phase --- zokrates_ast/src/typed/folder.rs | 4 +- zokrates_ast/src/typed/identifier.rs | 76 ++- zokrates_ast/src/typed/mod.rs | 2 +- zokrates_ast/src/typed/result_folder.rs | 4 +- zokrates_ast/src/typed/types.rs | 8 +- zokrates_ast/src/typed/uint.rs | 10 +- zokrates_ast/src/zir/uint.rs | 6 - .../{compile_errors => }/shadowing.zok | 0 zokrates_core/src/semantics.rs | 517 +++++++++--------- .../static_analysis/condition_redefiner.rs | 4 +- .../reducer/constants_reader.rs | 69 +-- .../src/static_analysis/reducer/inline.rs | 10 +- .../src/static_analysis/reducer/mod.rs | 9 +- .../static_analysis/reducer/shallow_ssa.rs | 139 ++++- 14 files changed, 536 insertions(+), 322 deletions(-) rename zokrates_cli/examples/{compile_errors => }/shadowing.zok (100%) diff --git a/zokrates_ast/src/typed/folder.rs b/zokrates_ast/src/typed/folder.rs index e957768a..d43d8728 100644 --- a/zokrates_ast/src/typed/folder.rs +++ b/zokrates_ast/src/typed/folder.rs @@ -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> { diff --git a/zokrates_ast/src/typed/identifier.rs b/zokrates_ast/src/typed/identifier.rs index 2eb64390..d6192c4d 100644 --- a/zokrates_ast/src/typed/identifier.rs +++ b/zokrates_ast/src/typed/identifier.rs @@ -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> 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> 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> 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> for Identifier<'ast> { + fn from(id: ShadowedIdentifier<'ast>) -> Identifier<'ast> { + Identifier { id, version: 0 } } } impl<'ast> From> for Identifier<'ast> { fn from(id: CoreIdentifier<'ast>) -> Identifier<'ast> { - Identifier { id, version: 0 } + Identifier::from(ShadowedIdentifier::from(id)) } } diff --git a/zokrates_ast/src/typed/mod.rs b/zokrates_ast/src/typed/mod.rs index 9c3f5e12..39cbd3c0 100644 --- a/zokrates_ast/src/typed/mod.rs +++ b/zokrates_ast/src/typed/mod.rs @@ -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, diff --git a/zokrates_ast/src/typed/result_folder.rs b/zokrates_ast/src/typed/result_folder.rs index 4e922054..191fb461 100644 --- a/zokrates_ast/src/typed/result_folder.rs +++ b/zokrates_ast/src/typed/result_folder.rs @@ -156,14 +156,14 @@ pub trait ResultFolder<'ast, T: Field>: Sized { } fn fold_name(&mut self, n: Identifier<'ast>) -> Result, 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, Self::Error> { diff --git a/zokrates_ast/src/typed/types.rs b/zokrates_ast/src/typed/types.rs index 18cd658c..2454c425 100644 --- a/zokrates_ast/src/typed/types.rs +++ b/zokrates_ast/src/typed/types.rs @@ -229,13 +229,13 @@ impl<'ast, T> From> 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>( impl<'ast, T> From> 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; diff --git a/zokrates_ast/src/typed/uint.rs b/zokrates_ast/src/typed/uint.rs index 51579e6b..eeab8eb0 100644 --- a/zokrates_ast/src/typed/uint.rs +++ b/zokrates_ast/src/typed/uint.rs @@ -127,11 +127,11 @@ impl<'ast, T: Field> From 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 { diff --git a/zokrates_ast/src/zir/uint.rs b/zokrates_ast/src/zir/uint.rs index 0198ba71..fc0b1d52 100644 --- a/zokrates_ast/src/zir/uint.rs +++ b/zokrates_ast/src/zir/uint.rs @@ -84,12 +84,6 @@ impl<'ast, T: Field> From 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 for UExpression<'ast, T> { fn from(u: u32) -> Self { UExpressionInner::Value(u as u128).annotate(UBitwidth::B32) diff --git a/zokrates_cli/examples/compile_errors/shadowing.zok b/zokrates_cli/examples/shadowing.zok similarity index 100% rename from zokrates_cli/examples/compile_errors/shadowing.zok rename to zokrates_cli/examples/shadowing.zok diff --git a/zokrates_core/src/semantics.rs b/zokrates_core/src/semantics.rs index 4661de0b..e5e39980 100644 --- a/zokrates_core/src/semantics.rs +++ b/zokrates_core/src/semantics.rs @@ -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(&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, IdentifierInfo<'ast, T>>; +#[derive(Default, Debug)] +struct Scope<'ast, T> { + level: usize, + map: HashMap, BTreeMap>>>, +} + +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>> { + 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>, scope: Scope<'ast, T>, functions: HashSet>, - 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( - declaration.id, - module_id.into(), - )), zokrates_ast::typed::types::try_from_g_type(ty.clone()).unwrap())); + + let id = declaration.id; + let info = IdentifierInfo { + 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 .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, Vec> { + + 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, Vec> { - let assignee_type = assignee.get_type(); - + ) -> Result, 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])?; + + // 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])?; - - let assignee = TypedAssignee::Identifier(var); - - self.check_assignment_inner(assignee, expr, pos, module_id, types) + .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])?; + + 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) => { 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()) - .annotate(bitwidth) - .into()), - Type::FieldElement => { - Ok(FieldElementExpression::Identifier(id.id.into()).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.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 { 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>, ) -> 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 = new_with_args(scope, 1, HashSet::new()); + let mut checker: Checker = 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 = new_with_args(HashMap::new(), 0, functions); + let mut checker: Checker = 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 = new_with_args(HashMap::new(), 0, HashSet::new()); + let mut checker: Checker = 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::::new(vec![((*MODULE_ID).clone(), module)].into_iter().collect()); - let mut checker: Checker = new_with_args(HashMap::new(), 0, HashSet::new()); + let mut checker: Checker = 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::::new(vec![((*MODULE_ID).clone(), module)].into_iter().collect()); - let mut checker: Checker = new_with_args(HashMap::new(), 0, HashSet::new()); + let mut checker: Checker = 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 = new_with_args(HashMap::new(), 0, HashSet::new()); + let mut checker: Checker = 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 = new_with_args(HashMap::new(), 0, HashSet::new()); + let mut checker: Checker = 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 = new_with_args(HashMap::new(), 0, HashSet::new()); + let mut checker: Checker = 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 = Checker::default(); let _: Result, Vec> = 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 = Checker::default(); let _: Result, Vec> = 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 ); } diff --git a/zokrates_core/src/static_analysis/condition_redefiner.rs b/zokrates_core/src/static_analysis/condition_redefiner.rs index 0820345f..244a2a01 100644 --- a/zokrates_core/src/static_analysis/condition_redefiner.rs +++ b/zokrates_core/src/static_analysis/condition_redefiner.rs @@ -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(), diff --git a/zokrates_core/src/static_analysis/reducer/constants_reader.rs b/zokrates_core/src/static_analysis/reducer/constants_reader.rs index 1d6ff6b4..c2bd802e 100644 --- a/zokrates_core/src/static_analysis/reducer/constants_reader.rs +++ b/zokrates_core/src/static_analysis/reducer/constants_reader.rs @@ -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: CoreIdentifier::Constant(c), + 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: CoreIdentifier::Constant(c), + 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: CoreIdentifier::Constant(c), + 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: CoreIdentifier::Constant(c), + 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: CoreIdentifier::Constant(c), + 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: CoreIdentifier::Constant(c), + 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), diff --git a/zokrates_core/src/static_analysis/reducer/inline.rs b/zokrates_core/src/static_analysis/reducer/inline.rs index 0b897364..a096499a 100644 --- a/zokrates_core/src/static_analysis/reducer/inline.rs +++ b/zokrates_core/src/static_analysis/reducer/inline.rs @@ -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; diff --git a/zokrates_core/src/static_analysis/reducer/mod.rs b/zokrates_core/src/static_analysis/reducer/mod.rs index 218ccaf6..07949177 100644 --- a/zokrates_core/src/static_analysis/reducer/mod.rs +++ b/zokrates_core/src/static_analysis/reducer/mod.rs @@ -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, TypedExpression<'ast, T>>; // An SSA version map, giving access to the latest version number for each identifier -pub type Versions<'ast> = HashMap, usize>; +pub type Versions<'ast> = HashMap, 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, HashMap>); +struct Substitutions<'ast>(HashMap, HashMap>); 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), ); diff --git a/zokrates_core/src/static_analysis/reducer/shallow_ssa.rs b/zokrates_core/src/static_analysis/reducer/shallow_ssa.rs index fe7d6573..0bba1027 100644 --- a/zokrates_core/src/static_analysis/reducer/shallow_ssa.rs +++ b/zokrates_core/src/static_analysis/reducer/shallow_ssa.rs @@ -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 = 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 = 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 = 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 = 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;