enforce explicit types in defnitions and arrays

This commit is contained in:
collin 2020-04-24 12:34:09 -07:00
parent 9e163428a9
commit 59cf617a08
8 changed files with 138 additions and 124 deletions

View File

@ -1,10 +1,8 @@
function test() -> (u32, u32) { function main() -> (u32[3]) {
return 4, 4 a = [1, 2]
} c = 3
u32[3] b = [...a, c]
function main() -> (u32, u32) {
a, b = test() return b
return a, b
} }

View File

@ -1,4 +1,4 @@
//! Methods to enforce constraints a resolved aleo program. //! Methods to enforce constraints and construct a resolved aleo program.
use crate::ast; use crate::ast;
use crate::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue}; use crate::constraints::{new_scope_from_variable, ResolvedProgram, ResolvedValue};
@ -98,13 +98,26 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
function.returns.to_owned(), function.returns.to_owned(),
) )
} }
Statement::MultipleDefinition(assignees, function_call) => self Statement::Assign(variable, expression) => {
.enforce_multiple_definition_statement( self.enforce_assign_statement(cs, function.get_name(), variable, expression);
}
Statement::Definition(ty, assignee, expression) => {
self.enforce_definition_statement(
cs,
function.get_name(),
ty,
assignee,
expression,
);
}
Statement::MultipleDefinition(assignees, function_call) => {
self.enforce_multiple_definition_statement(
cs, cs,
function.get_name(), function.get_name(),
assignees, assignees,
function_call, function_call,
), );
}
Statement::For(index, start, stop, statements) => { Statement::For(index, start, stop, statements) => {
self.enforce_for_statement( self.enforce_for_statement(
cs, cs,
@ -115,14 +128,6 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
statements, statements,
); );
} }
Statement::Definition(variable, expression) => {
self.enforce_definition_statement(
cs,
function.get_name(),
variable,
expression,
);
}
}); });
return_values return_values
@ -140,9 +145,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
.for_each(|(i, parameter)| { .for_each(|(i, parameter)| {
// append each variable to arguments vector // append each variable to arguments vector
arguments.push(Expression::Variable(match parameter.ty { arguments.push(Expression::Variable(match parameter.ty {
Type::U32 => { Type::U32 => self.u32_from_parameter(cs, function.get_name(), i + 1, parameter),
self.integer_from_parameter(cs, function.get_name(), i + 1, parameter)
}
Type::FieldElement => { Type::FieldElement => {
self.field_element_from_parameter(cs, function.get_name(), i + 1, parameter) self.field_element_from_parameter(cs, function.get_name(), i + 1, parameter)
} }
@ -150,12 +153,9 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
self.bool_from_parameter(cs, function.get_name(), i + 1, parameter) self.bool_from_parameter(cs, function.get_name(), i + 1, parameter)
} }
Type::Array(ref ty, _length) => match *ty.clone() { Type::Array(ref ty, _length) => match *ty.clone() {
Type::U32 => self.integer_array_from_parameter( Type::U32 => {
cs, self.u32_array_from_parameter(cs, function.get_name(), i + 1, parameter)
function.get_name(), }
i + 1,
parameter,
),
Type::FieldElement => self.field_element_array_from_parameter( Type::FieldElement => self.field_element_array_from_parameter(
cs, cs,
function.get_name(), function.get_name(),

View File

@ -10,7 +10,7 @@ use snarkos_models::gadgets::{
}; };
impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> { impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
pub(crate) fn integer_from_parameter( pub(crate) fn u32_from_parameter(
&mut self, &mut self,
cs: &mut CS, cs: &mut CS,
scope: String, scope: String,
@ -46,7 +46,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
parameter_variable parameter_variable
} }
pub(crate) fn integer_array_from_parameter( pub(crate) fn u32_array_from_parameter(
&mut self, &mut self,
_cs: &mut CS, _cs: &mut CS,
_scope: String, _scope: String,

View File

@ -27,7 +27,15 @@ impl<F: Field + PrimeField> ResolvedValue<F> {
(ResolvedValue::U32(ref _a), Type::U32) => true, (ResolvedValue::U32(ref _a), Type::U32) => true,
(ResolvedValue::FieldElement(ref _a), Type::FieldElement) => true, (ResolvedValue::FieldElement(ref _a), Type::FieldElement) => true,
(ResolvedValue::Boolean(ref _a), Type::Boolean) => true, (ResolvedValue::Boolean(ref _a), Type::Boolean) => true,
(ResolvedValue::Array(ref arr), Type::Array(ref _ty, ref len)) => arr.len() == *len, // todo: add array types (ResolvedValue::Array(ref arr), Type::Array(ref ty, ref len)) => {
// check array lengths are equal
let mut res = arr.len() == *len;
// check each value in array matches
for value in arr {
res &= value.match_type(ty)
}
res
}
( (
ResolvedValue::StructExpression(ref actual_name, ref _members), ResolvedValue::StructExpression(ref actual_name, ref _members),
Type::Struct(ref expected_name), Type::Struct(ref expected_name),

View File

@ -17,7 +17,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
} }
} }
fn enforce_definition( fn store_assignment(
&mut self, &mut self,
cs: &mut CS, cs: &mut CS,
scope: String, scope: String,
@ -117,7 +117,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
} }
} }
pub(crate) fn enforce_definition_statement( pub(crate) fn enforce_assign_statement(
&mut self, &mut self,
cs: &mut CS, cs: &mut CS,
scope: String, scope: String,
@ -126,7 +126,24 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
) { ) {
let result_value = &mut self.enforce_expression(cs, scope.clone(), expression); let result_value = &mut self.enforce_expression(cs, scope.clone(), expression);
self.enforce_definition(cs, scope, assignee, result_value); self.store_assignment(cs, scope, assignee, result_value);
}
pub(crate) fn enforce_definition_statement(
&mut self,
cs: &mut CS,
scope: String,
ty: Type<F>,
assignee: Assignee<F>,
expression: Expression<F>,
) {
let result_value = &mut self.enforce_expression(cs, scope.clone(), expression);
if result_value.match_type(&ty) {
self.store_assignment(cs, scope, assignee, result_value);
} else {
unimplemented!("incompatible types {} = {}", assignee, result_value)
}
} }
pub(crate) fn enforce_multiple_definition_statement( pub(crate) fn enforce_multiple_definition_statement(
@ -149,7 +166,7 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
.into_iter() .into_iter()
.zip(return_values.into_iter()) .zip(return_values.into_iter())
.for_each(|(assignee, mut return_value)| { .for_each(|(assignee, mut return_value)| {
self.enforce_definition(cs, scope.clone(), assignee, &mut return_value); self.store_assignment(cs, scope.clone(), assignee, &mut return_value);
}); });
} }
@ -188,14 +205,17 @@ impl<F: Field + PrimeField, CS: ConstraintSystem<F>> ResolvedProgram<F, CS> {
// TODO: add support for early termination // TODO: add support for early termination
let _res = self.enforce_return_statement(cs, scope, statements, return_types); let _res = self.enforce_return_statement(cs, scope, statements, return_types);
} }
Statement::For(index, start, stop, statements) => { Statement::Assign(variable, expression) => {
self.enforce_for_statement(cs, scope, index, start, stop, statements); self.enforce_assign_statement(cs, scope, variable, expression);
}
Statement::Definition(ty, assignee, expression) => {
self.enforce_definition_statement(cs, scope, ty, assignee, expression);
} }
Statement::MultipleDefinition(assignees, function) => { Statement::MultipleDefinition(assignees, function) => {
self.enforce_multiple_definition_statement(cs, scope, assignees, function); self.enforce_multiple_definition_statement(cs, scope, assignees, function);
} }
Statement::Definition(variable, expression) => { Statement::For(index, start, stop, statements) => {
self.enforce_definition_statement(cs, scope, variable, expression); self.enforce_for_statement(cs, scope, index, start, stop, statements);
} }
}; };
} }

View File

@ -97,17 +97,7 @@ pub enum Assignee<F: Field + PrimeField> {
StructMember(Box<Assignee<F>>, Variable<F>), StructMember(Box<Assignee<F>>, Variable<F>),
} }
/// Program statement that defines some action (or expression) to be carried out. /// Explicit type used for defining a variable or expression type
#[derive(Clone)]
pub enum Statement<F: Field + PrimeField> {
// Declaration(Variable),
Return(Vec<Expression<F>>),
Definition(Assignee<F>, Expression<F>),
For(Variable<F>, Integer, Integer, Vec<Statement<F>>),
MultipleDefinition(Vec<Assignee<F>>, Expression<F>),
}
/// Explicit type used for defining struct members and function parameters
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Type<F: Field + PrimeField> { pub enum Type<F: Field + PrimeField> {
U32, U32,
@ -117,6 +107,17 @@ pub enum Type<F: Field + PrimeField> {
Struct(Variable<F>), Struct(Variable<F>),
} }
/// Program statement that defines some action (or expression) to be carried out.
#[derive(Clone)]
pub enum Statement<F: Field + PrimeField> {
// Declaration(Variable),
Return(Vec<Expression<F>>),
Assign(Assignee<F>, Expression<F>),
Definition(Type<F>, Assignee<F>, Expression<F>),
MultipleDefinition(Vec<Assignee<F>>, Expression<F>),
For(Variable<F>, Integer, Integer, Vec<Statement<F>>),
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct StructMember<F: Field + PrimeField> { pub struct StructMember<F: Field + PrimeField> {
pub variable: Variable<F>, pub variable: Variable<F>,

View File

@ -151,6 +151,12 @@ impl<F: Field + PrimeField> fmt::Display for Statement<F> {
} }
write!(f, "\n") write!(f, "\n")
} }
Statement::Assign(ref variable, ref statement) => {
write!(f, "{} = {}", variable, statement)
}
Statement::Definition(ref ty, ref assignee, ref statement) => {
write!(f, "{} {} = {}", ty, assignee, statement)
}
Statement::MultipleDefinition(ref assignees, ref function) => { Statement::MultipleDefinition(ref assignees, ref function) => {
for (i, id) in assignees.iter().enumerate() { for (i, id) in assignees.iter().enumerate() {
write!(f, "{}", id)?; write!(f, "{}", id)?;
@ -167,9 +173,6 @@ impl<F: Field + PrimeField> fmt::Display for Statement<F> {
} }
write!(f, "\tendfor") write!(f, "\tendfor")
} }
Statement::Definition(ref variable, ref statement) => {
write!(f, "{} = {}", variable, statement)
}
} }
} }
} }
@ -187,6 +190,12 @@ impl<F: Field + PrimeField> fmt::Debug for Statement<F> {
} }
write!(f, "\n") write!(f, "\n")
} }
Statement::Assign(ref variable, ref statement) => {
write!(f, "{} = {}", variable, statement)
}
Statement::Definition(ref ty, ref assignee, ref statement) => {
write!(f, "{} {} = {}", ty, assignee, statement)
}
Statement::MultipleDefinition(ref assignees, ref function) => { Statement::MultipleDefinition(ref assignees, ref function) => {
for (i, id) in assignees.iter().enumerate() { for (i, id) in assignees.iter().enumerate() {
write!(f, "{}", id)?; write!(f, "{}", id)?;
@ -203,9 +212,6 @@ impl<F: Field + PrimeField> fmt::Debug for Statement<F> {
} }
write!(f, "\tendfor") write!(f, "\tendfor")
} }
Statement::Definition(ref variable, ref statement) => {
write!(f, "{} = {}", variable, statement)
}
} }
} }
} }

View File

@ -192,6 +192,50 @@ impl<'ast, F: Field + PrimeField> From<ast::TernaryExpression<'ast>> for types::
} }
} }
impl<'ast, F: Field + PrimeField> From<ast::ArrayInlineExpression<'ast>> for types::Expression<F> {
fn from(array: ast::ArrayInlineExpression<'ast>) -> Self {
types::Expression::Array(
array
.expressions
.into_iter()
.map(|s_or_e| Box::new(types::SpreadOrExpression::from(s_or_e)))
.collect(),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::ArrayInitializerExpression<'ast>>
for types::Expression<F>
{
fn from(array: ast::ArrayInitializerExpression<'ast>) -> Self {
let count = types::Expression::<F>::get_count(array.count);
let expression = Box::new(types::SpreadOrExpression::from(*array.expression));
types::Expression::Array(vec![expression; count])
}
}
impl<'ast, F: Field + PrimeField> From<ast::InlineStructMember<'ast>> for types::StructMember<F> {
fn from(member: ast::InlineStructMember<'ast>) -> Self {
types::StructMember {
variable: types::Variable::from(member.variable),
expression: types::Expression::from(member.expression),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::StructInlineExpression<'ast>> for types::Expression<F> {
fn from(expression: ast::StructInlineExpression<'ast>) -> Self {
let variable = types::Variable::from(expression.variable);
let members = expression
.members
.into_iter()
.map(|member| types::StructMember::from(member))
.collect::<Vec<types::StructMember<F>>>();
types::Expression::Struct(variable, members)
}
}
impl<'ast, F: Field + PrimeField> From<ast::PostfixExpression<'ast>> for types::Expression<F> { impl<'ast, F: Field + PrimeField> From<ast::PostfixExpression<'ast>> for types::Expression<F> {
fn from(expression: ast::PostfixExpression<'ast>) -> Self { fn from(expression: ast::PostfixExpression<'ast>) -> Self {
let variable = types::Expression::Variable(types::Variable::from(expression.variable)); let variable = types::Expression::Variable(types::Variable::from(expression.variable));
@ -229,28 +273,6 @@ impl<'ast, F: Field + PrimeField> From<ast::PostfixExpression<'ast>> for types::
} }
} }
impl<'ast, F: Field + PrimeField> From<ast::ArrayInlineExpression<'ast>> for types::Expression<F> {
fn from(array: ast::ArrayInlineExpression<'ast>) -> Self {
types::Expression::Array(
array
.expressions
.into_iter()
.map(|s_or_e| Box::new(types::SpreadOrExpression::from(s_or_e)))
.collect(),
)
}
}
impl<'ast, F: Field + PrimeField> From<ast::ArrayInitializerExpression<'ast>>
for types::Expression<F>
{
fn from(array: ast::ArrayInitializerExpression<'ast>) -> Self {
let count = types::Expression::<F>::get_count(array.count);
let expression = Box::new(types::SpreadOrExpression::from(*array.expression));
types::Expression::Array(vec![expression; count])
}
}
impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Expression<F> { impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Expression<F> {
fn from(expression: ast::Expression<'ast>) -> Self { fn from(expression: ast::Expression<'ast>) -> Self {
match expression { match expression {
@ -261,18 +283,13 @@ impl<'ast, F: Field + PrimeField> From<ast::Expression<'ast>> for types::Express
ast::Expression::Ternary(expression) => types::Expression::from(expression), ast::Expression::Ternary(expression) => types::Expression::from(expression),
ast::Expression::ArrayInline(expression) => types::Expression::from(expression), ast::Expression::ArrayInline(expression) => types::Expression::from(expression),
ast::Expression::ArrayInitializer(expression) => types::Expression::from(expression), ast::Expression::ArrayInitializer(expression) => types::Expression::from(expression),
ast::Expression::StructInline(_expression) => { ast::Expression::StructInline(expression) => types::Expression::from(expression),
unimplemented!("unknown type for inline struct expression")
}
ast::Expression::Postfix(expression) => types::Expression::from(expression), ast::Expression::Postfix(expression) => types::Expression::from(expression),
// _ => unimplemented!(), // _ => unimplemented!(),
} }
} }
} }
/// pest ast -> typed types::Expression
/// For defined types (ex: u32[4]) we manually construct the expression instead of implementing the From trait.
/// This saves us from having to resolve things at a later point in time.
impl<'ast, F: Field + PrimeField> types::Expression<F> { impl<'ast, F: Field + PrimeField> types::Expression<F> {
fn get_count(count: ast::Value<'ast>) -> usize { fn get_count(count: ast::Value<'ast>) -> usize {
match count { match count {
@ -284,34 +301,6 @@ impl<'ast, F: Field + PrimeField> types::Expression<F> {
size => unimplemented!("Array size should be an integer {}", size), size => unimplemented!("Array size should be an integer {}", size),
} }
} }
fn from_struct(ty: ast::StructType<'ast>, expression: ast::Expression<'ast>) -> Self {
let declaration_struct = ty.variable.value;
match expression {
ast::Expression::StructInline(inline_struct) => {
if inline_struct.variable.value != declaration_struct {
unimplemented!("Declared struct type must match inline struct type")
}
let variable = types::Variable::from(inline_struct.variable);
let members = inline_struct
.members
.into_iter()
.map(|member| types::StructMember::from(member))
.collect::<Vec<types::StructMember<F>>>();
types::Expression::Struct(variable, members)
}
_ => unimplemented!("Struct declaration must be followed by inline struct"),
}
}
fn from_type(ty: ast::Type<'ast>, expression: ast::Expression<'ast>) -> Self {
match ty {
ast::Type::Basic(_ty) => Self::from(expression),
ast::Type::Array(_ty) => Self::from(expression),
ast::Type::Struct(ty) => Self::from_struct(ty, expression),
}
}
} }
/// pest ast -> types::Assignee /// pest ast -> types::Assignee
@ -407,7 +396,7 @@ impl<'ast, F: Field + PrimeField> From<ast::MultipleAssignmentStatement<'ast>>
impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::Statement<F> { impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::AssignStatement<'ast>) -> Self { fn from(statement: ast::AssignStatement<'ast>) -> Self {
types::Statement::Definition( types::Statement::Assign(
types::Assignee::from(statement.assignee), types::Assignee::from(statement.assignee),
types::Expression::from(statement.expression), types::Expression::from(statement.expression),
) )
@ -417,8 +406,9 @@ impl<'ast, F: Field + PrimeField> From<ast::AssignStatement<'ast>> for types::St
impl<'ast, F: Field + PrimeField> From<ast::DefinitionStatement<'ast>> for types::Statement<F> { impl<'ast, F: Field + PrimeField> From<ast::DefinitionStatement<'ast>> for types::Statement<F> {
fn from(statement: ast::DefinitionStatement<'ast>) -> Self { fn from(statement: ast::DefinitionStatement<'ast>) -> Self {
types::Statement::Definition( types::Statement::Definition(
types::Type::from(statement.ty),
types::Assignee::from(statement.variable), types::Assignee::from(statement.variable),
types::Expression::from_type(statement.ty, statement.expression), types::Expression::from(statement.expression),
) )
} }
} }
@ -474,15 +464,6 @@ impl<'ast, F: Field + PrimeField> From<ast::Type<'ast>> for types::Type<F> {
/// pest ast -> types::Struct /// pest ast -> types::Struct
impl<'ast, F: Field + PrimeField> From<ast::InlineStructMember<'ast>> for types::StructMember<F> {
fn from(member: ast::InlineStructMember<'ast>) -> Self {
types::StructMember {
variable: types::Variable::from(member.variable),
expression: types::Expression::from(member.expression),
}
}
}
impl<'ast, F: Field + PrimeField> From<ast::StructField<'ast>> for types::StructField<F> { impl<'ast, F: Field + PrimeField> From<ast::StructField<'ast>> for types::StructField<F> {
fn from(struct_field: ast::StructField<'ast>) -> Self { fn from(struct_field: ast::StructField<'ast>) -> Self {
types::StructField { types::StructField {