1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

gracefully handle unconstrained variables

This commit is contained in:
dark64 2021-08-22 23:22:15 +02:00
parent 9cce1d9ef2
commit 5a208beea7
3 changed files with 54 additions and 33 deletions

View file

@ -189,15 +189,16 @@ pub fn compile<T: Field, E: Into<imports::Error>>(
) -> Result<CompilationArtifacts<T>, CompileErrors> {
let arena = Arena::new();
let (typed_ast, abi) = check_with_arena(source, location, resolver, config, &arena)?;
let (typed_ast, abi) =
check_with_arena(source, location.to_path_buf(), resolver, config, &arena)?;
// flatten input program
log::debug!("Flatten");
let program_flattened = Flattener::flatten(typed_ast, config);
// analyse (constant propagation after call resolution)
log::debug!("Analyse flat program");
let program_flattened = program_flattened.analyse();
// constant propagation after call resolution
log::debug!("Propagate flat program");
let program_flattened = program_flattened.propagate();
// convert to ir
log::debug!("Convert to IR");
@ -207,9 +208,11 @@ pub fn compile<T: Field, E: Into<imports::Error>>(
log::debug!("Optimise IR");
let optimized_ir_prog = ir_prog.optimize();
// analyse (check constraints)
// analyse ir (check constraints)
log::debug!("Analyse IR");
let optimized_ir_prog = optimized_ir_prog.analyse();
let optimized_ir_prog = optimized_ir_prog
.analyse()
.map_err(|e| CompileErrorInner::from(e).in_file(location.as_path()))?;
Ok(CompilationArtifacts {
prog: optimized_ir_prog,

View file

@ -33,13 +33,18 @@ use std::fmt;
use zokrates_field::Field;
pub trait Analyse {
fn analyse(self) -> Self;
type Error;
fn analyse(self) -> Result<Self, Self::Error>
where
Self: Sized;
}
#[derive(Debug)]
pub enum Error {
Reducer(self::reducer::Error),
Propagation(self::propagation::Error),
NonConstantArgument(self::constant_argument_checker::Error),
UnconstrainedVariable(self::unconstrained_vars::Error),
}
impl From<reducer::Error> for Error {
@ -60,12 +65,19 @@ impl From<constant_argument_checker::Error> for Error {
}
}
impl From<unconstrained_vars::Error> for Error {
fn from(e: unconstrained_vars::Error) -> Self {
Error::UnconstrainedVariable(e)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::Reducer(e) => write!(f, "{}", e),
Error::Propagation(e) => write!(f, "{}", e),
Error::NonConstantArgument(e) => write!(f, "{}", e),
Error::UnconstrainedVariable(e) => write!(f, "{}", e),
}
}
}
@ -126,16 +138,11 @@ impl<'ast, T: Field> TypedProgram<'ast, T> {
}
}
impl<T: Field> Analyse for FlatProg<T> {
fn analyse(self) -> Self {
log::debug!("Static analyser: Propagate flat");
self.propagate()
}
}
impl<T: Field> Analyse for Prog<T> {
fn analyse(self) -> Self {
type Error = Error;
fn analyse(self) -> Result<Self, Self::Error> {
log::debug!("Static analyser: Detect unconstrained zir");
UnconstrainedVariableDetector::detect(self)
UnconstrainedVariableDetector::detect(self).map_err(|e| e.into())
}
}

View file

@ -3,6 +3,7 @@ use crate::ir::folder::Folder;
use crate::ir::Directive;
use crate::ir::Prog;
use std::collections::HashSet;
use std::fmt;
use zokrates_field::Field;
#[derive(Debug)]
@ -10,6 +11,20 @@ pub struct UnconstrainedVariableDetector {
pub(self) variables: HashSet<FlatVariable>,
}
#[derive(Debug)]
pub struct Error(usize);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Found unconstrained variables during IR analysis (found {} occurrence{})",
self.0,
if self.0 == 1 { "" } else { "s" }
)
}
}
impl UnconstrainedVariableDetector {
fn new<T: Field>(p: &Prog<T>) -> Self {
UnconstrainedVariableDetector {
@ -21,22 +36,16 @@ impl UnconstrainedVariableDetector {
.collect(),
}
}
pub fn detect<T: Field>(p: Prog<T>) -> Prog<T> {
pub fn detect<T: Field>(p: Prog<T>) -> Result<Prog<T>, Error> {
let mut instance = Self::new(&p);
let p = instance.fold_module(p);
// we should probably handle this case instead of asserting at some point
assert!(
instance.variables.is_empty(),
"Unconstrained variables are not allowed (found {} occurrence{})",
instance.variables.len(),
if instance.variables.len() == 1 {
""
} else {
"s"
}
);
p
if instance.variables.is_empty() {
Ok(p)
} else {
Err(Error(instance.variables.len()))
}
}
}
@ -63,7 +72,6 @@ mod tests {
use zokrates_field::Bn128Field;
#[test]
#[should_panic]
fn should_detect_unconstrained_private_input() {
// def main(_0) -> (1):
// (1 * ~one) * (42 * ~one) == 1 * ~out_0
@ -92,7 +100,8 @@ mod tests {
main,
};
UnconstrainedVariableDetector::detect(p);
let p = UnconstrainedVariableDetector::detect(p);
assert!(p.is_err());
}
#[test]
@ -116,7 +125,8 @@ mod tests {
main,
};
UnconstrainedVariableDetector::detect(p);
let p = UnconstrainedVariableDetector::detect(p);
assert!(p.is_ok());
}
#[test]
@ -174,6 +184,7 @@ mod tests {
main,
};
UnconstrainedVariableDetector::detect(p);
let p = UnconstrainedVariableDetector::detect(p);
assert!(p.is_ok());
}
}