Merge pull request #65 from AleoHQ/feature/const

Feature/const
This commit is contained in:
Collin Chin 2020-06-17 22:50:23 -07:00 committed by GitHub
commit ad8223a8a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 250 additions and 72 deletions

View File

@ -29,6 +29,27 @@ graph LR
2. Circuit definitions
3. Function definitions
## Defining Variables
Leo supports `let` and `const` keywords for variable definition.
```let a = true;``` defines an **allocated** program variable `a` with boolean value `true`.
```const a = true;``` defines a **constant** program variable `a` with boolean value `true`.
**Allocated** variables define private variables in the constraint system. Their value is constrained in the circuit on initialization.
**Constant** variables do not define a variable in the constraint system. Their value is constrained in the circuit on computation with an **allocated** variable.
**Constant** variables can be mutable. They do not have the same functionality as `const` variables in other languages.
```rust
function addOne() -> {
let a = 0u8; // allocated, value enforced on this line
const b = 1u8; // constant, value not enforced yet
return a + b // allocated, computed value is enforced to be the sum of both values
}
```
Computations are expressed in terms of arithmetic circuits, in particular rank-1 quadratic constraint systems. Thus computing on an allocated variable always results in another allocated variable.
## Mutability
* All defined variables in Leo are immutable by default.
* Variables can be made mutable with the `mut` keyword.

18
ast/src/common/declare.rs Normal file
View File

@ -0,0 +1,18 @@
use crate::ast::Rule;
use pest_ast::FromPest;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::declare))]
pub enum Declare {
Const(Const),
Let(Let),
}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::const_))]
pub struct Const {}
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::let_))]
pub struct Let {}

View File

@ -1,6 +1,9 @@
pub mod assignee;
pub use assignee::*;
pub mod declare;
pub use declare::*;
pub mod eoi;
pub use eoi::*;

View File

@ -8,7 +8,11 @@ file = { SOI ~ NEWLINE* ~ import* ~ NEWLINE* ~ circuit_definition* ~ NEWLINE* ~
// Declared in common/identifier.rs
identifier = @{ ((!protected_name ~ ASCII_ALPHA) | (protected_name ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* }
protected_name = { "let" | "for"| "if" | "else" | "as" | "return" }
protected_name = {
"for"| "if" | "else" | "as" | "return"
| declare | mutable | static_ | value_boolean
| type_array | type_data
}
// Declared in common/line_end.rs
LINE_END = { ";" ~ NEWLINE* }
@ -36,6 +40,11 @@ static_ = { "static" }
// Declared in common/variable.rs
variable = { mutable? ~ identifier ~ (":" ~ type_)? }
// Declared in common/declare.rs
declare = { let_ | const_ }
const_ = { "const" }
let_ = { "let" }
/// Operations
// Declared in operations/not_operation.rs
@ -249,7 +258,7 @@ statement_conditional = {"if" ~ (expression | "(" ~ expression ~ ")") ~ "{" ~ NE
conditional_nested_or_end_statement = { statement_conditional | "{" ~ NEWLINE* ~ statement+ ~ "}"}
// Declared in statements/definition_statement.rs
statement_definition = { "let" ~ variable ~ "=" ~ expression ~ LINE_END}
statement_definition = { declare ~ variable ~ "=" ~ expression ~ LINE_END}
// Declared in statements/expression_statement.rs
statement_expression = { expression ~ LINE_END }
@ -258,7 +267,7 @@ statement_expression = { expression ~ LINE_END }
statement_for = { "for" ~ identifier ~ "in" ~ expression ~ ".." ~ expression ~ "{" ~ NEWLINE* ~ statement+ ~ "}"}
// Declared in statements/multiple_assignment_statement.rs
statement_multiple_assignment = { "let" ~ "(" ~ variable_tuple ~ ")" ~ "=" ~ identifier ~ "(" ~ expression_tuple ~ ")" ~ LINE_END}
statement_multiple_assignment = { declare ~ "(" ~ variable_tuple ~ ")" ~ "=" ~ identifier ~ "(" ~ expression_tuple ~ ")" ~ LINE_END}
variable_tuple = _{ variable ~ ("," ~ variable)* }
// Declared in statements/return_statement.rs

View File

@ -1,6 +1,6 @@
use crate::{
ast::Rule,
common::{LineEnd, Variable},
common::{Declare, LineEnd, Variable},
expressions::Expression,
};
@ -11,6 +11,7 @@ use std::fmt;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_definition))]
pub struct DefinitionStatement<'ast> {
pub declare: Declare,
pub variable: Variable<'ast>,
pub expression: Expression<'ast>,
pub line_end: LineEnd,

View File

@ -1,6 +1,6 @@
use crate::{
ast::Rule,
common::{Identifier, LineEnd, Variable},
common::{Declare, Identifier, LineEnd, Variable},
expressions::Expression,
};
@ -11,6 +11,7 @@ use std::fmt;
#[derive(Clone, Debug, FromPest, PartialEq)]
#[pest_ast(rule(Rule::statement_multiple_assignment))]
pub struct MultipleAssignmentStatement<'ast> {
pub declare: Declare,
pub variables: Vec<Variable<'ast>>,
pub function_name: Identifier<'ast>,
pub arguments: Vec<Expression<'ast>>,

View File

@ -10,6 +10,7 @@ use leo_types::{
Assignee,
ConditionalNestedOrEndStatement,
ConditionalStatement,
Declare,
Expression,
Identifier,
Integer,
@ -221,6 +222,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
cs: &mut CS,
file_scope: String,
function_scope: String,
declare: Declare,
variable: Variable,
expression: Expression,
) -> Result<(), StatementError> {
@ -228,7 +230,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
if let Some(ref _type) = variable._type {
expected_types.push(_type.clone());
}
let value = self.enforce_expression(
let mut value = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
@ -236,6 +238,10 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
expression,
)?;
if let Declare::Let = declare {
value.allocate_value(cs)?;
}
self.store_definition(function_scope, variable, value)
}
@ -484,8 +490,8 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
Statement::Return(expressions) => {
res = Some(self.enforce_return_statement(cs, file_scope, function_scope, expressions, return_types)?);
}
Statement::Definition(variable, expression) => {
self.enforce_definition_statement(cs, file_scope, function_scope, variable, expression)?;
Statement::Definition(declare, variable, expression) => {
self.enforce_definition_statement(cs, file_scope, function_scope, declare, variable, expression)?;
}
Statement::Assign(variable, expression) => {
self.enforce_assign_statement(cs, file_scope, function_scope, indicator, variable, expression)?;

View File

@ -9,6 +9,7 @@ use snarkos_models::{
gadgets::{
r1cs::ConstraintSystem,
utilities::{
alloc::AllocGadget,
boolean::Boolean,
eq::ConditionalEqGadget,
select::CondSelectGadget,
@ -105,6 +106,74 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
*self = *inner.clone()
}
}
pub(crate) fn allocate_value<CS: ConstraintSystem<F>>(&mut self, mut cs: CS) -> Result<(), ValueError> {
match self {
// allocated values
ConstrainedValue::Boolean(boolean) => {
let option = boolean.get_value();
*boolean = Boolean::alloc(cs, || option.ok_or(SynthesisError::AssignmentMissing))?;
}
ConstrainedValue::Integer(integer) => {
let integer_type = integer.get_type();
let option = integer.get_value();
let name = format!("clone {}", integer);
*integer = Integer::allocate_type(&mut cs, integer_type, name, option)?;
}
ConstrainedValue::Field(field) => {
let option = field.get_value().map(|v| format!("{}", v));
*field = FieldType::alloc(cs, || option.ok_or(SynthesisError::AssignmentMissing))?;
}
ConstrainedValue::Group(group) => {
let string = format!("{}", group); // may need to implement u256 -> decimal formatting
*group = G::alloc(cs, || Ok(string))?;
}
// value wrappers
ConstrainedValue::Array(array) => {
array
.iter_mut()
.enumerate()
.map(|(i, value)| value.allocate_value(cs.ns(|| format!("allocate array member {}", i))))
.collect::<Result<(), ValueError>>()?;
}
ConstrainedValue::CircuitExpression(_id, members) => {
members
.iter_mut()
.enumerate()
.map(|(i, member)| {
member
.1
.allocate_value(cs.ns(|| format!("allocate circuit member {}", i)))
})
.collect::<Result<(), ValueError>>()?;
}
ConstrainedValue::Return(array) => {
array
.iter_mut()
.enumerate()
.map(|(i, value)| value.allocate_value(cs.ns(|| format!("allocate return member {}", i))))
.collect::<Result<(), ValueError>>()?;
}
ConstrainedValue::Mutable(value) => {
value.allocate_value(cs)?;
}
ConstrainedValue::Static(value) => {
value.allocate_value(cs)?;
}
// empty wrappers
ConstrainedValue::CircuitDefinition(_) => {}
ConstrainedValue::Function(_, _) => {}
ConstrainedValue::Unresolved(_) => {
return Err(ValueError::SynthesisError(SynthesisError::AssignmentMissing));
}
}
Ok(())
}
}
impl<F: Field + PrimeField, G: GroupType<F>> fmt::Display for ConstrainedValue<F, G> {

View File

@ -1,6 +1,7 @@
use crate::errors::{FieldError, GroupError};
use leo_types::IntegerError;
use snarkos_errors::gadgets::SynthesisError;
use std::{num::ParseIntError, str::ParseBoolError};
#[derive(Debug, Error)]
@ -19,4 +20,7 @@ pub enum ValueError {
#[error("{}", _0)]
ParseIntError(#[from] ParseIntError),
#[error("{}", _0)]
SynthesisError(#[from] SynthesisError),
}

View File

@ -1,8 +1,8 @@
// Multidimensional array syntax in leo
function main() -> u32[3][2] {
let m = [[0u32, 0u32, 0u32], [0u32, 0u32, 0u32]]; // inline
const m = [[0u32, 0u32, 0u32], [0u32, 0u32, 0u32]]; // inline
let m: u32[3][2] = [[0; 3]; 2]; // initializer
const m: u32[3][2] = [[0; 3]; 2]; // initializer
return m
}

View File

@ -1,8 +1,8 @@
// !(true && (false || true))
function main() -> bool {
let a = true;
let b = false || a;
let c = !(true && b);
const a = true;
const b = false || a;
const c = !(true && b);
return c
}

View File

@ -3,6 +3,6 @@ function foo() -> field {
}
function main() -> field {
let myGlobal = 42field;
const myGlobal = 42field;
return foo()
}

View File

@ -12,7 +12,7 @@ function bad_mutate(mut x: u32) {
}
function main() -> u32 {
let a = 1;
let a = 1u32;
bad_mutate(a);
return a // <- returns 1

View File

@ -1,6 +1,6 @@
function main() -> group {
let point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
let point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
const point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
return point_1 + point_2
}

View File

@ -1,6 +1,6 @@
function main() {
let point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
let point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
const point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
assert_eq!(point_1, point_2);
}

View File

@ -1,6 +1,6 @@
function main() {
let point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
let point_2 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_2 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
assert_eq!(point_1, point_2);
}

View File

@ -1,6 +1,6 @@
function main() -> bool {
let point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
let point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
const point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
return point_1 == point_2
}

View File

@ -1,6 +1,6 @@
function main() -> bool {
let point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
let point_2 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_2 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
return point_1 == point_2
}

View File

@ -1,6 +1,6 @@
function main() -> group {
let point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
let point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
const point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
return point_1 - point_2
}

View File

@ -1,6 +1,6 @@
function main (b: bool) -> group {
let point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
let point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
const point_1 = (7374112779530666882856915975292384652154477718021969292781165691637980424078, 3435195339177955418892975564890903138308061187980579490487898366607011481796)group;
const point_2 = (1005842117974384149622370061042978581211342111653966059496918451529532134799, 79389132189982034519597104273449021362784864778548730890166152019533697186)group;
return if b ? point_1 : point_2
}

View File

@ -0,0 +1,27 @@
use leo_ast::common::Declare as AstDeclare;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Declare {
Const,
Let,
}
impl<'ast> From<AstDeclare> for Declare {
fn from(declare: AstDeclare) -> Self {
match declare {
AstDeclare::Const(_) => Declare::Const,
AstDeclare::Let(_) => Declare::Let,
}
}
}
impl fmt::Display for Declare {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Declare::Const => write!(f, "const"),
Declare::Let => write!(f, "let"),
}
}
}

View File

@ -1,6 +1,9 @@
pub mod assignee;
pub use assignee::*;
pub mod declare;
pub use declare::*;
pub mod identifier;
pub use identifier::*;

View File

@ -58,16 +58,20 @@ impl<'ast> Integer {
}
impl Integer {
pub fn to_usize(&self) -> usize {
pub fn get_value(&self) -> Option<u128> {
match self {
Integer::U8(u8) => u8.value.unwrap() as usize,
Integer::U16(u16) => u16.value.unwrap() as usize,
Integer::U32(u32) => u32.value.unwrap() as usize,
Integer::U64(u64) => u64.value.unwrap() as usize,
Integer::U128(u128) => u128.value.unwrap() as usize,
Integer::U8(u8) => u8.value.map(|v| v as u128),
Integer::U16(u16) => u16.value.map(|v| v as u128),
Integer::U32(u32) => u32.value.map(|v| v as u128),
Integer::U64(u64) => u64.value.map(|v| v as u128),
Integer::U128(u128) => u128.value.map(|v| v as u128),
}
}
pub fn to_usize(&self) -> usize {
self.get_value().unwrap() as usize
}
pub fn get_type(&self) -> IntegerType {
match self {
Integer::U8(_u8) => IntegerType::U8,
@ -78,6 +82,47 @@ impl Integer {
}
}
pub fn allocate_type<F: Field, CS: ConstraintSystem<F>>(
cs: &mut CS,
integer_type: IntegerType,
name: String,
option: Option<u128>,
) -> Result<Self, IntegerError> {
Ok(match integer_type {
IntegerType::U8 => {
let u8_option = option.map(|integer| integer as u8);
let u8_result = UInt8::alloc(cs.ns(|| name), || u8_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U8(u8_result)
}
IntegerType::U16 => {
let u16_option = option.map(|integer| integer as u16);
let u16_result = UInt16::alloc(cs.ns(|| name), || u16_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U16(u16_result)
}
IntegerType::U32 => {
let u32_option = option.map(|integer| integer as u32);
let u32_result = UInt32::alloc(cs.ns(|| name), || u32_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U32(u32_result)
}
IntegerType::U64 => {
let u64_option = option.map(|integer| integer as u64);
let u64_result = UInt64::alloc(cs.ns(|| name), || u64_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U64(u64_result)
}
IntegerType::U128 => {
let u128_option = option.map(|integer| integer as u128);
let u128_result =
UInt128::alloc(cs.ns(|| name), || u128_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U128(u128_result)
}
})
}
pub fn from_input<F: Field, CS: ConstraintSystem<F>>(
cs: &mut CS,
integer_type: IntegerType,
@ -85,7 +130,7 @@ impl Integer {
integer_value: Option<InputValue>,
) -> Result<Self, IntegerError> {
// Check that the input value is the correct type
let integer_option = match integer_value {
let option = match integer_value {
Some(input) => {
if let InputValue::Integer(_type_, integer) = input {
Some(integer)
@ -96,39 +141,7 @@ impl Integer {
None => None,
};
Ok(match integer_type {
IntegerType::U8 => {
let u8_option = integer_option.map(|integer| integer as u8);
let u8_result = UInt8::alloc(cs.ns(|| name), || u8_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U8(u8_result)
}
IntegerType::U16 => {
let u16_option = integer_option.map(|integer| integer as u16);
let u16_result = UInt16::alloc(cs.ns(|| name), || u16_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U16(u16_result)
}
IntegerType::U32 => {
let u32_option = integer_option.map(|integer| integer as u32);
let u32_result = UInt32::alloc(cs.ns(|| name), || u32_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U32(u32_result)
}
IntegerType::U64 => {
let u64_option = integer_option.map(|integer| integer as u64);
let u64_result = UInt64::alloc(cs.ns(|| name), || u64_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U64(u64_result)
}
IntegerType::U128 => {
let u128_option = integer_option.map(|integer| integer as u128);
let u128_result =
UInt128::alloc(cs.ns(|| name), || u128_option.ok_or(SynthesisError::AssignmentMissing))?;
Integer::U128(u128_result)
}
})
Self::allocate_type(cs, integer_type, name, option)
}
pub fn add<F: Field + PrimeField, CS: ConstraintSystem<F>>(

View File

@ -1,4 +1,4 @@
use crate::{Assignee, ConditionalStatement, Expression, Identifier, Integer, Variable};
use crate::{Assignee, ConditionalStatement, Declare, Expression, Identifier, Integer, Variable};
use leo_ast::{
operations::AssignOperation,
statements::{
@ -19,7 +19,7 @@ use std::fmt;
#[derive(Clone, PartialEq, Eq)]
pub enum Statement {
Return(Vec<Expression>),
Definition(Variable, Expression),
Definition(Declare, Variable, Expression),
Assign(Assignee, Expression),
MultipleAssign(Vec<Variable>, Expression),
Conditional(ConditionalStatement),
@ -43,6 +43,7 @@ impl<'ast> From<ReturnStatement<'ast>> for Statement {
impl<'ast> From<DefinitionStatement<'ast>> for Statement {
fn from(statement: DefinitionStatement<'ast>) -> Self {
Statement::Definition(
Declare::from(statement.declare),
Variable::from(statement.variable),
Expression::from(statement.expression),
)
@ -176,7 +177,9 @@ impl fmt::Display for Statement {
}
write!(f, ")\n")
}
Statement::Definition(ref variable, ref expression) => write!(f, "let {} = {};", variable, expression),
Statement::Definition(ref declare, ref variable, ref expression) => {
write!(f, "{} {} = {};", declare, variable, expression)
}
Statement::Assign(ref variable, ref statement) => write!(f, "{} = {};", variable, statement),
Statement::MultipleAssign(ref assignees, ref function) => {
write!(f, "let (")?;