implict ternary expression

This commit is contained in:
collin 2020-05-18 11:43:45 -07:00
parent 2b48224689
commit d0c88a08be
5 changed files with 136 additions and 52 deletions

View File

@ -1,11 +1,3 @@
circuit Foo {
x: u32
static function new() -> Self {
return Self { x: 5 }
}
}
function main() {
let a = Foo::new();
let a: u32 = if true ? 1 : 5;
}

View File

@ -10,12 +10,15 @@ use crate::{
CircuitFieldDefinition, CircuitMember, Expression, Identifier, RangeOrExpression,
SpreadOrExpression,
},
Type,
Integer, Type,
};
use snarkos_models::{
curves::{Field, Group, PrimeField},
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
gadgets::{
r1cs::ConstraintSystem,
utilities::{boolean::Boolean, select::CondSelectGadget},
},
};
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
@ -382,6 +385,54 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
}
/// Enforce ternary conditional expression
fn enforce_conditional_expression(
&mut self,
cs: &mut CS,
file_scope: String,
function_scope: String,
expected_types: Vec<Type<F, G>>,
first: Expression<F, G>,
second: Expression<F, G>,
third: Expression<F, G>,
) -> Result<ConstrainedValue<F, G>, ExpressionError> {
let resolved_first = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
vec![Type::Boolean],
first,
)? {
ConstrainedValue::Boolean(resolved) => resolved,
value => return Err(ExpressionError::IfElseConditional(value.to_string())),
};
let resolved_second = self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
second,
)?;
let resolved_third =
self.enforce_expression(cs, file_scope, function_scope, expected_types, third)?;
match (resolved_second, resolved_third) {
(ConstrainedValue::Boolean(bool_2), ConstrainedValue::Boolean(bool_3)) => {
let result = Boolean::conditionally_select(cs, &resolved_first, &bool_2, &bool_3)?;
Ok(ConstrainedValue::Boolean(result))
}
(ConstrainedValue::Integer(integer_2), ConstrainedValue::Integer(integer_3)) => {
let result =
Integer::conditionally_select(cs, &resolved_first, &integer_2, &integer_3)?;
Ok(ConstrainedValue::Integer(result))
}
(_, _) => {
unimplemented!("conditional select gadget not implemented between given types")
}
}
}
/// Enforce array expressions
fn enforce_array_expression(
&mut self,
@ -976,24 +1027,15 @@ impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgra
}
// Conditionals
Expression::IfElse(first, second, third) => {
let resolved_first = match self.enforce_expression(
cs,
file_scope.clone(),
function_scope.clone(),
expected_types.clone(),
*first,
)? {
ConstrainedValue::Boolean(resolved) => resolved,
value => return Err(ExpressionError::IfElseConditional(value.to_string())),
};
if resolved_first.eq(&Boolean::Constant(true)) {
self.enforce_expression(cs, file_scope, function_scope, expected_types, *second)
} else {
self.enforce_expression(cs, file_scope, function_scope, expected_types, *third)
}
}
Expression::IfElse(first, second, third) => self.enforce_conditional_expression(
cs,
file_scope,
function_scope,
expected_types,
*first,
*second,
*third,
),
// Arrays
Expression::Array(array) => {

View File

@ -7,11 +7,72 @@ use crate::{
IntegerType,
};
use snarkos_errors::gadgets::SynthesisError;
use snarkos_models::{
curves::{Field, Group, PrimeField},
gadgets::{r1cs::ConstraintSystem, utilities::boolean::Boolean},
gadgets::{
r1cs::ConstraintSystem,
utilities::{
boolean::Boolean, select::CondSelectGadget, uint128::UInt128, uint16::UInt16,
uint32::UInt32, uint64::UInt64, uint8::UInt8,
},
},
};
impl Integer {
pub fn to_usize(&self) -> usize {
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,
}
}
pub(crate) fn get_type(&self) -> IntegerType {
match self {
Integer::U8(_u8) => IntegerType::U8,
Integer::U16(_u16) => IntegerType::U16,
Integer::U32(_u32) => IntegerType::U32,
Integer::U64(_u64) => IntegerType::U64,
Integer::U128(_u128) => IntegerType::U128,
}
}
}
impl<F: Field + PrimeField> CondSelectGadget<F> for Integer {
fn conditionally_select<CS: ConstraintSystem<F>>(
cs: CS,
cond: &Boolean,
first: &Self,
second: &Self,
) -> Result<Self, SynthesisError> {
match (first, second) {
(Integer::U8(u8_first), Integer::U8(u8_second)) => Ok(Integer::U8(
UInt8::conditionally_select(cs, cond, u8_first, u8_second)?,
)),
(Integer::U16(u16_first), Integer::U16(u18_second)) => Ok(Integer::U16(
UInt16::conditionally_select(cs, cond, u16_first, u18_second)?,
)),
(Integer::U32(u32_first), Integer::U32(u32_second)) => Ok(Integer::U32(
UInt32::conditionally_select(cs, cond, u32_first, u32_second)?,
)),
(Integer::U64(u64_first), Integer::U64(u64_second)) => Ok(Integer::U64(
UInt64::conditionally_select(cs, cond, u64_first, u64_second)?,
)),
(Integer::U128(u128_first), Integer::U128(u128_second)) => Ok(Integer::U128(
UInt128::conditionally_select(cs, cond, u128_first, u128_second)?,
)),
(_, _) => Err(SynthesisError::Unsatisfiable), // types do not match
}
}
fn cost() -> usize {
unimplemented!("Cannot calculate cost.")
}
}
impl<F: Field + PrimeField, G: Group, CS: ConstraintSystem<F>> ConstrainedProgram<F, G, CS> {
pub(crate) fn get_integer_constant(integer: Integer) -> ConstrainedValue<F, G> {
ConstrainedValue::Integer(integer)

View File

@ -1,5 +1,7 @@
use crate::errors::{BooleanError, FieldElementError, FunctionError, IntegerError, ValueError};
use snarkos_errors::gadgets::SynthesisError;
#[derive(Debug, Error)]
pub enum ExpressionError {
// Identifiers
@ -85,6 +87,15 @@ pub enum ExpressionError {
// Conditionals
#[error("If, else conditional must resolve to a boolean, got {}", _0)]
IfElseConditional(String),
#[error("{}", _0)]
SynthesisError(SynthesisError),
}
impl From<SynthesisError> for ExpressionError {
fn from(error: SynthesisError) -> Self {
ExpressionError::SynthesisError(error)
}
}
impl From<ValueError> for ExpressionError {

View File

@ -54,28 +54,6 @@ pub enum Integer {
U128(UInt128),
}
impl Integer {
pub fn to_usize(&self) -> usize {
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,
}
}
pub(crate) fn get_type(&self) -> IntegerType {
match self {
Integer::U8(_u8) => IntegerType::U8,
Integer::U16(_u16) => IntegerType::U16,
Integer::U32(_u32) => IntegerType::U32,
Integer::U64(_u64) => IntegerType::U64,
Integer::U128(_u128) => IntegerType::U128,
}
}
}
/// A constant or allocated element in the field
#[derive(Clone, PartialEq, Eq)]
pub enum FieldElement<F: Field + PrimeField> {