mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-18 15:31:32 +03:00
fix recursive circuit member function namespace bug
This commit is contained in:
parent
3f10bcfe82
commit
84f634559c
152
README.md
152
README.md
@ -130,14 +130,14 @@ function main() -> group {
|
|||||||
### Operator Assignment Statements
|
### Operator Assignment Statements
|
||||||
```rust
|
```rust
|
||||||
function main() -> u32 {
|
function main() -> u32 {
|
||||||
let mut a = 10;
|
let mut a = 10;
|
||||||
a += 5;
|
a += 5;
|
||||||
a -= 10;
|
a -= 10;
|
||||||
a *= 5;
|
a *= 5;
|
||||||
a /= 5;
|
a /= 5;
|
||||||
a **= 2;
|
a **= 2;
|
||||||
|
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -173,11 +173,11 @@ function main() -> u32[2] {
|
|||||||
### Multidimensional Arrays
|
### Multidimensional Arrays
|
||||||
```rust
|
```rust
|
||||||
function main() -> u32[3][2] {
|
function main() -> u32[3][2] {
|
||||||
let m = [[0u32, 0u32], [0u32, 0u32]];
|
let m = [[0u32, 0u32], [0u32, 0u32]];
|
||||||
|
|
||||||
let m: u32[3][2] = [[0; 3]; 2];
|
let m: u32[3][2] = [[0; 3]; 2];
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -193,8 +193,8 @@ In the underlying circuit, this is a single bit multiplexer.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
function main() -> u32 {
|
function main() -> u32 {
|
||||||
let y = if 3==3 ? 1 : 5;
|
let y = if 3==3 ? 1 : 5;
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -204,45 +204,47 @@ Since `first` and `second` are one or more statements, they resolve to separate
|
|||||||
In the underlying circuit this can be thought of as a demultiplexer.
|
In the underlying circuit this can be thought of as a demultiplexer.
|
||||||
```rust
|
```rust
|
||||||
function main(a: bool, b: bool) -> u32 {
|
function main(a: bool, b: bool) -> u32 {
|
||||||
let mut res = 0u32;
|
let mut res = 0u32;
|
||||||
if a {
|
|
||||||
res = 1;
|
if a {
|
||||||
} else if b {
|
res = 1;
|
||||||
res = 2;
|
} else if b {
|
||||||
} else {
|
res = 2;
|
||||||
res = 3;
|
} else {
|
||||||
}
|
res = 3;
|
||||||
return res
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### For loop
|
### For loop
|
||||||
```rust
|
```rust
|
||||||
function main() -> fe {
|
function main() -> fe {
|
||||||
let mut a = 1field;
|
let mut a = 1field;
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
a = a + 1;
|
a = a + 1;
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
```rust
|
```rust
|
||||||
function test1(a : u32) -> u32 {
|
function test1(a : u32) -> u32 {
|
||||||
return a + 1
|
return a + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function test2(b: fe) -> field {
|
function test2(b: fe) -> field {
|
||||||
return b * 2field
|
return b * 2field
|
||||||
}
|
}
|
||||||
|
|
||||||
function test3(c: bool) -> bool {
|
function test3(c: bool) -> bool {
|
||||||
return c && true
|
return c && true
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() -> u32 {
|
function main() -> u32 {
|
||||||
return test1(5)
|
return test1(5)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -250,13 +252,13 @@ function main() -> u32 {
|
|||||||
### Function Scope
|
### Function Scope
|
||||||
```rust
|
```rust
|
||||||
function foo() -> field {
|
function foo() -> field {
|
||||||
// return myGlobal <- not allowed
|
// return myGlobal <- not allowed
|
||||||
return 42field
|
return 42field
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() -> field {
|
function main() -> field {
|
||||||
let myGlobal = 42field;
|
let myGlobal = 42field;
|
||||||
return foo()
|
return foo()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -279,7 +281,7 @@ Main function inputs are allocated private variables in the program's constraint
|
|||||||
`a` is implicitly private.
|
`a` is implicitly private.
|
||||||
```rust
|
```rust
|
||||||
function main(a: field) -> field {
|
function main(a: field) -> field {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Normal function inputs are passed by value.
|
Normal function inputs are passed by value.
|
||||||
@ -299,8 +301,8 @@ function main() -> u32 {
|
|||||||
## Circuits
|
## Circuits
|
||||||
Circuits in Leo are similar to classes in object oriented langauges. Circuits are defined above functions in a Leo program. Circuits can have one or more members.
|
Circuits in Leo are similar to classes in object oriented langauges. Circuits are defined above functions in a Leo program. Circuits can have one or more members.
|
||||||
|
|
||||||
|
#### Circuit member values
|
||||||
Members can be defined as fields which hold primitive values
|
Members can be defined as fields which hold primitive values.
|
||||||
```rust
|
```rust
|
||||||
circuit Point {
|
circuit Point {
|
||||||
x: u32
|
x: u32
|
||||||
@ -312,51 +314,73 @@ function main() -> u32 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Circuit member functions
|
||||||
Members can also be defined as functions.
|
Members can also be defined as functions.
|
||||||
```rust
|
```rust
|
||||||
circuit Circ {
|
circuit Foo {
|
||||||
function echo(x: u32) -> u32 {
|
function echo(x: u32) -> u32 {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() -> u32 {
|
function main() -> u32 {
|
||||||
let c = Circ { };
|
let c = Foo { };
|
||||||
return c.echo(1u32)
|
return c.echo(1u32)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Circuit member static functions
|
||||||
Circuit functions can be made static, enabling them to be called without instantiation.
|
Circuit functions can be made static, enabling them to be called without instantiation.
|
||||||
```rust
|
```rust
|
||||||
circuit Circ {
|
circuit Foo {
|
||||||
static function echo(x: u32) -> u32 {
|
static function echo(x: u32) -> u32 {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() -> u32 {
|
function main() -> u32 {
|
||||||
return Circ::echo(1u32)
|
return Foo::echo(1u32)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `Self` keyword is supported in circuit functions.
|
#### `Self` and `self`
|
||||||
|
The `Self` keyword references the circuit definition.
|
||||||
```rust
|
```rust
|
||||||
circuit Circ {
|
circuit Foo {
|
||||||
b: bool
|
b: bool
|
||||||
|
|
||||||
static function new() -> Self {
|
static function new() -> Self { // Self resolves to Foo
|
||||||
return Self { b: true }
|
return Self { b: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() -> Circ {
|
function main() -> bool {
|
||||||
let c = Circ::new();
|
let c = Foo::new();
|
||||||
return c.b
|
return c.b
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `self` keyword references the circuit's members.
|
||||||
|
```rust
|
||||||
|
circuit Foo {
|
||||||
|
b: bool
|
||||||
|
|
||||||
|
function bar() -> bool {
|
||||||
|
return self.b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() -> bool {
|
||||||
|
let c = Foo { b: true };
|
||||||
|
return c.b
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Imports
|
## Imports
|
||||||
Leo supports importing functions and circuits by name into the current file with the following syntax:
|
Leo supports importing functions
|
||||||
|
}
|
||||||
|
} and circuits by name into the current file with the following syntax:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
import [package].[name];
|
import [package].[name];
|
||||||
@ -472,11 +496,11 @@ This will enforce that the two values are equal in the constraint system.
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
function main() {
|
function main() {
|
||||||
assert_eq!(45, 45);
|
assert_eq!(45, 45);
|
||||||
|
|
||||||
assert_eq!(2fe, 2fe);
|
assert_eq!(2fe, 2fe);
|
||||||
|
|
||||||
assert_eq!(true, true);
|
assert_eq!(true, true);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -490,15 +514,15 @@ function main(a: u32) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test function expect_pass() {
|
test function expect_pass() {
|
||||||
let a = 1u32;
|
let a = 1u32;
|
||||||
|
|
||||||
let res = main(a);
|
let res = main(a);
|
||||||
|
|
||||||
assert_eq!(res, 1u32);
|
assert_eq!(res, 1u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
test function expect_fail() {
|
test function expect_fail() {
|
||||||
assert_eq!(1u8, 0u8);
|
assert_eq!(1u8, 0u8);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -704,9 +704,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
// access a circuit member using the `self` keyword
|
// access a circuit member using the `self` keyword
|
||||||
if let Expression::Identifier(ref identifier) = *circuit_identifier {
|
if let Expression::Identifier(ref identifier) = *circuit_identifier {
|
||||||
if identifier.is_self() {
|
if identifier.is_self() {
|
||||||
let self_keyword = new_scope(function_scope, SELF_KEYWORD.to_string());
|
let self_file_scope = new_scope(file_scope.clone(), identifier.name.to_string());
|
||||||
|
let self_function_scope = new_scope(self_file_scope.clone(), identifier.name.to_string());
|
||||||
|
|
||||||
let member_value =
|
let member_value =
|
||||||
self.evaluate_identifier(file_scope, self_keyword, &vec![], circuit_member.clone())?;
|
self.evaluate_identifier(self_file_scope, self_function_scope, &vec![], circuit_member.clone())?;
|
||||||
|
|
||||||
return Ok(member_value);
|
return Ok(member_value);
|
||||||
}
|
}
|
||||||
@ -730,20 +732,13 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
Some(member) => {
|
Some(member) => {
|
||||||
match &member.1 {
|
match &member.1 {
|
||||||
ConstrainedValue::Function(ref _circuit_identifier, ref _function) => {
|
ConstrainedValue::Function(ref _circuit_identifier, ref _function) => {
|
||||||
// Pass static circuit fields into function call by value
|
// Pass circuit members into function call by value
|
||||||
for stored_member in members {
|
for stored_member in members {
|
||||||
match &stored_member.1 {
|
let circuit_scope = new_scope(file_scope.clone(), circuit_name.to_string());
|
||||||
ConstrainedValue::Function(_, _) => {}
|
let self_keyword = new_scope(circuit_scope, SELF_KEYWORD.to_string());
|
||||||
ConstrainedValue::Static(_) => {}
|
let field = new_scope(self_keyword, stored_member.0.to_string());
|
||||||
_ => {
|
|
||||||
let circuit_scope = new_scope(file_scope.clone(), circuit_name.to_string());
|
|
||||||
let function_scope = new_scope(circuit_scope, member.0.to_string());
|
|
||||||
let self_keyword = new_scope(function_scope, SELF_KEYWORD.to_string());
|
|
||||||
let field = new_scope(self_keyword, stored_member.0.to_string());
|
|
||||||
|
|
||||||
self.store(field, stored_member.1.clone());
|
self.store(field, stored_member.1.clone());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstrainedValue::Static(value) => {
|
ConstrainedValue::Static(value) => {
|
||||||
@ -831,7 +826,7 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
|||||||
*function.clone(),
|
*function.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (outer_scope, function_call) = function_value.extract_function(file_scope, span.clone())?;
|
let (outer_scope, function_call) = function_value.extract_function(file_scope.clone(), span.clone())?;
|
||||||
|
|
||||||
let name_unique = format!(
|
let name_unique = format!(
|
||||||
"function call {} {}:{}",
|
"function call {} {}:{}",
|
||||||
|
@ -14,6 +14,10 @@ pub fn new_scope(outer: String, inner: String) -> String {
|
|||||||
format!("{}_{}", outer, inner)
|
format!("{}_{}", outer, inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_in_scope(current_scope: &String, desired_scope: &String) -> bool {
|
||||||
|
current_scope.ends_with(desired_scope)
|
||||||
|
}
|
||||||
|
|
||||||
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedProgram<F, G> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
constraints::boolean::{allocate_bool, new_bool_constant},
|
constraints::boolean::{allocate_bool, new_bool_constant},
|
||||||
errors::{ExpressionError, FieldError, ValueError},
|
errors::{ExpressionError, FieldError, ValueError},
|
||||||
|
is_in_scope,
|
||||||
new_scope,
|
new_scope,
|
||||||
FieldType,
|
FieldType,
|
||||||
GroupType,
|
GroupType,
|
||||||
@ -122,8 +123,11 @@ impl<F: Field + PrimeField, G: GroupType<F>> ConstrainedValue<F, G> {
|
|||||||
ConstrainedValue::Function(circuit_identifier, function) => {
|
ConstrainedValue::Function(circuit_identifier, function) => {
|
||||||
let mut outer_scope = scope.clone();
|
let mut outer_scope = scope.clone();
|
||||||
// If this is a circuit function, evaluate inside the circuit scope
|
// If this is a circuit function, evaluate inside the circuit scope
|
||||||
if circuit_identifier.is_some() {
|
if let Some(identifier) = circuit_identifier {
|
||||||
outer_scope = new_scope(scope, circuit_identifier.unwrap().to_string());
|
// avoid creating recursive scope
|
||||||
|
if !is_in_scope(&scope, &identifier.name.to_string()) {
|
||||||
|
outer_scope = new_scope(scope, identifier.name.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((outer_scope, function.clone()))
|
Ok((outer_scope, function.clone()))
|
||||||
|
17
compiler/tests/circuits/member_function_nested.leo
Normal file
17
compiler/tests/circuits/member_function_nested.leo
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
circuit Foo {
|
||||||
|
x: u32,
|
||||||
|
|
||||||
|
function add_x(y: u32) -> u32 {
|
||||||
|
return self.x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
function call_add_x(y: u32) -> u32 {
|
||||||
|
return self.add_x(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() -> u32 {
|
||||||
|
let f = Foo { x: 1u32 };
|
||||||
|
|
||||||
|
return f.call_add_x(1u32)
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
get_error,
|
get_error,
|
||||||
get_output,
|
get_output,
|
||||||
integers::u32::output_one,
|
integers::u32::{output_number, output_one},
|
||||||
parse_program,
|
parse_program,
|
||||||
EdwardsConstrainedValue,
|
EdwardsConstrainedValue,
|
||||||
EdwardsTestCompiler,
|
EdwardsTestCompiler,
|
||||||
@ -138,6 +138,14 @@ fn test_member_function_invalid() {
|
|||||||
expect_fail(program);
|
expect_fail(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_member_function_nested() {
|
||||||
|
let bytes = include_bytes!("member_function_nested.leo");
|
||||||
|
let program = parse_program(bytes).unwrap();
|
||||||
|
|
||||||
|
output_number(program, 2u32);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_member_static_function() {
|
fn test_member_static_function() {
|
||||||
let bytes = include_bytes!("member_static_function.leo");
|
let bytes = include_bytes!("member_static_function.leo");
|
||||||
|
Loading…
Reference in New Issue
Block a user