gracefully handle unconstrained variables
This commit is contained in:
parent
9cce1d9ef2
commit
5a208beea7
3 changed files with 54 additions and 33 deletions
|
@ -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,
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue