mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-24 07:48:04 +03:00
add dynamic check errors for circuits 1
This commit is contained in:
parent
40d26dce7f
commit
973e2a6afc
@ -14,18 +14,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::{assert_satisfied, expect_compiler_error, parse_program, EdwardsTestCompiler};
|
use crate::{assert_satisfied, expect_compiler_error, expect_dynamic_check_error, parse_program, EdwardsTestCompiler};
|
||||||
use leo_compiler::errors::{CompilerError, ExpressionError, FunctionError, StatementError};
|
use leo_compiler::errors::{CompilerError, ExpressionError, FunctionError, StatementError};
|
||||||
|
|
||||||
fn expect_fail(program: EdwardsTestCompiler) {
|
|
||||||
match expect_compiler_error(program) {
|
|
||||||
CompilerError::FunctionError(FunctionError::StatementError(StatementError::ExpressionError(
|
|
||||||
ExpressionError::Error(_string),
|
|
||||||
))) => {}
|
|
||||||
error => panic!("Expected invalid circuit member error, got {}", error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expressions
|
// Expressions
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -41,15 +32,15 @@ fn test_inline_fail() {
|
|||||||
let bytes = include_bytes!("inline_fail.leo");
|
let bytes = include_bytes!("inline_fail.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let program = parse_program(bytes).unwrap();
|
||||||
|
|
||||||
expect_fail(program);
|
expect_compiler_error(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inline_undefined() {
|
fn test_inline_undefined() {
|
||||||
let bytes = include_bytes!("inline_undefined.leo");
|
let bytes = include_bytes!("inline_undefined.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let error = parse_program(bytes).err().unwrap();
|
||||||
|
|
||||||
expect_fail(program);
|
expect_dynamic_check_error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
@ -65,9 +56,9 @@ fn test_member_variable() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_member_variable_fail() {
|
fn test_member_variable_fail() {
|
||||||
let bytes = include_bytes!("member_variable_fail.leo");
|
let bytes = include_bytes!("member_variable_fail.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let error = parse_program(bytes).err().unwrap();
|
||||||
|
|
||||||
expect_fail(program);
|
expect_dynamic_check_error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -89,17 +80,17 @@ fn test_member_function() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_member_function_fail() {
|
fn test_member_function_fail() {
|
||||||
let bytes = include_bytes!("member_function_fail.leo");
|
let bytes = include_bytes!("member_function_fail.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let error = parse_program(bytes).err().unwrap();
|
||||||
|
|
||||||
expect_fail(program);
|
expect_dynamic_check_error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_member_function_invalid() {
|
fn test_member_function_invalid() {
|
||||||
let bytes = include_bytes!("member_function_invalid.leo");
|
let bytes = include_bytes!("member_function_invalid.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let error = parse_program(bytes).err().unwrap();
|
||||||
|
|
||||||
expect_fail(program);
|
expect_dynamic_check_error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -129,17 +120,17 @@ fn test_member_static_function_nested() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_member_static_function_invalid() {
|
fn test_member_static_function_invalid() {
|
||||||
let bytes = include_bytes!("member_static_function_invalid.leo");
|
let bytes = include_bytes!("member_static_function_invalid.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let error = parse_program(bytes).err().unwrap();
|
||||||
|
|
||||||
expect_fail(program)
|
expect_dynamic_check_error(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_member_static_function_undefined() {
|
fn test_member_static_function_undefined() {
|
||||||
let bytes = include_bytes!("member_static_function_undefined.leo");
|
let bytes = include_bytes!("member_static_function_undefined.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let error = parse_program(bytes).err().unwrap();
|
||||||
|
|
||||||
expect_fail(program)
|
expect_dynamic_check_error(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mutability
|
// Mutability
|
||||||
@ -213,9 +204,9 @@ fn test_mutate_variable_fail() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_self_fail() {
|
fn test_self_fail() {
|
||||||
let bytes = include_bytes!("self_fail.leo");
|
let bytes = include_bytes!("self_fail.leo");
|
||||||
let program = parse_program(bytes).unwrap();
|
let error = parse_program(bytes).err().unwrap();
|
||||||
|
|
||||||
expect_compiler_error(program);
|
expect_dynamic_check_error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -696,6 +696,15 @@ impl Frame {
|
|||||||
/// Returns the type of the identifier in the symbol table.
|
/// Returns the type of the identifier in the symbol table.
|
||||||
///
|
///
|
||||||
fn parse_identifier(&self, identifier: &Identifier) -> Result<Type, FrameError> {
|
fn parse_identifier(&self, identifier: &Identifier) -> Result<Type, FrameError> {
|
||||||
|
// Check Self type.
|
||||||
|
if identifier.is_self_type() {
|
||||||
|
// Check for frame circuit self type.
|
||||||
|
let circuit_type = self.self_type_or_error(&identifier.span)?;
|
||||||
|
|
||||||
|
// Return new type with circuit identifier.
|
||||||
|
return Ok(Type::Circuit(circuit_type.identifier));
|
||||||
|
}
|
||||||
|
|
||||||
// Check variable symbol table.
|
// Check variable symbol table.
|
||||||
if let Some(type_) = self.get_variable(&identifier.name) {
|
if let Some(type_) = self.get_variable(&identifier.name) {
|
||||||
return Ok(type_.to_owned());
|
return Ok(type_.to_owned());
|
||||||
@ -730,7 +739,6 @@ impl Frame {
|
|||||||
right: &Expression,
|
right: &Expression,
|
||||||
span: &Span,
|
span: &Span,
|
||||||
) -> Result<Type, FrameError> {
|
) -> Result<Type, FrameError> {
|
||||||
println!("left {}, right {}", left, right);
|
|
||||||
// Get the left expression type.
|
// Get the left expression type.
|
||||||
let left_type = self.parse_expression(left)?;
|
let left_type = self.parse_expression(left)?;
|
||||||
|
|
||||||
@ -998,6 +1006,16 @@ impl Frame {
|
|||||||
Ok(*element_type)
|
Ok(*element_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the Self type of the frame or an error if it does not exist.
|
||||||
|
///
|
||||||
|
fn self_type_or_error(&self, span: &Span) -> Result<CircuitType, FrameError> {
|
||||||
|
self.self_type
|
||||||
|
.as_ref()
|
||||||
|
.map(|circuit_type| circuit_type.clone())
|
||||||
|
.ok_or_else(|| FrameError::circuit_self(span))
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Returns the type of inline circuit expression.
|
/// Returns the type of inline circuit expression.
|
||||||
///
|
///
|
||||||
@ -1009,14 +1027,15 @@ impl Frame {
|
|||||||
) -> Result<Type, FrameError> {
|
) -> Result<Type, FrameError> {
|
||||||
// Check if identifier is Self circuit type.
|
// Check if identifier is Self circuit type.
|
||||||
let circuit_type = if identifier.is_self() {
|
let circuit_type = if identifier.is_self() {
|
||||||
self.self_type.clone()
|
// Get the Self type of the frame.
|
||||||
|
self.self_type_or_error(span)?
|
||||||
} else {
|
} else {
|
||||||
// Get circuit type.
|
// Get circuit type.
|
||||||
self.user_defined_types
|
self.user_defined_types
|
||||||
.get_circuit(&identifier.name)
|
.get_circuit(&identifier.name)
|
||||||
.map(|circuit_type| circuit_type.clone())
|
.map(|circuit_type| circuit_type.clone())
|
||||||
}
|
.ok_or_else(|| FrameError::undefined_circuit(identifier))?
|
||||||
.unwrap();
|
};
|
||||||
|
|
||||||
// Check the length of the circuit members.
|
// Check the length of the circuit members.
|
||||||
if circuit_type.variables.len() != members.len() {
|
if circuit_type.variables.len() != members.len() {
|
||||||
@ -1064,7 +1083,7 @@ impl Frame {
|
|||||||
let circuit_type = self.parse_circuit_name(type_, span)?;
|
let circuit_type = self.parse_circuit_name(type_, span)?;
|
||||||
|
|
||||||
// Look for member with matching name.
|
// Look for member with matching name.
|
||||||
Ok(circuit_type.member_type(&identifier).unwrap())
|
Ok(circuit_type.member_type(&identifier)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1092,7 +1111,7 @@ impl Frame {
|
|||||||
// Lookup circuit identifier.
|
// Lookup circuit identifier.
|
||||||
self.user_defined_types.get_circuit(&identifier.name).unwrap()
|
self.user_defined_types.get_circuit(&identifier.name).unwrap()
|
||||||
}
|
}
|
||||||
_ => unimplemented!("expected circuit type"),
|
type_ => unimplemented!("expected circuit type {:?}", type_),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1143,7 +1162,9 @@ impl Frame {
|
|||||||
let circuit_type = self.parse_circuit_name(type_, span)?;
|
let circuit_type = self.parse_circuit_name(type_, span)?;
|
||||||
|
|
||||||
// Find circuit function by identifier.
|
// Find circuit function by identifier.
|
||||||
Ok(circuit_type.member_function_type(identifier).unwrap())
|
circuit_type
|
||||||
|
.member_function_type(identifier)
|
||||||
|
.ok_or_else(|| FrameError::undefined_circuit_function(identifier))
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1160,7 +1181,7 @@ impl Frame {
|
|||||||
|
|
||||||
// Check that the function is non-static.
|
// Check that the function is non-static.
|
||||||
if circuit_function_type.attributes.contains(&Attribute::Static) {
|
if circuit_function_type.attributes.contains(&Attribute::Static) {
|
||||||
unimplemented!("Called static function using dot syntax")
|
return Err(FrameError::invalid_static_access(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the function type.
|
// Return the function type.
|
||||||
@ -1181,7 +1202,7 @@ impl Frame {
|
|||||||
|
|
||||||
// Check that the function is static.
|
// Check that the function is static.
|
||||||
if !circuit_function_type.attributes.contains(&Attribute::Static) {
|
if !circuit_function_type.attributes.contains(&Attribute::Static) {
|
||||||
unimplemented!("Called non-static function using double colon syntax")
|
return Err(FrameError::invalid_member_access(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(circuit_function_type.function.to_owned())
|
Ok(circuit_function_type.function.to_owned())
|
||||||
@ -1199,7 +1220,6 @@ impl Frame {
|
|||||||
span: &Span,
|
span: &Span,
|
||||||
) -> Result<Type, FrameError> {
|
) -> Result<Type, FrameError> {
|
||||||
// Parse the function name.
|
// Parse the function name.
|
||||||
println!("expression {}", expression);
|
|
||||||
let function_type = self.parse_function_name(expression, span)?;
|
let function_type = self.parse_function_name(expression, span)?;
|
||||||
|
|
||||||
// Check the length of arguments
|
// Check the length of arguments
|
||||||
@ -1656,9 +1676,10 @@ impl TypeVariablePairs {
|
|||||||
match (left, right) {
|
match (left, right) {
|
||||||
(Type::TypeVariable(variable), type_) => Ok(self.push(variable, type_)),
|
(Type::TypeVariable(variable), type_) => Ok(self.push(variable, type_)),
|
||||||
(type_, Type::TypeVariable(variable)) => Ok(self.push(variable, type_)),
|
(type_, Type::TypeVariable(variable)) => Ok(self.push(variable, type_)),
|
||||||
(Type::Array(type1, dimensions1), Type::Array(type2, dimensions2)) => {
|
(Type::Array(left_type, left_dimensions), Type::Array(right_type, right_dimensions)) => {
|
||||||
self.push_pairs_array(type1, dimensions1, type2, dimensions2, span)
|
self.push_pairs_array(left_type, left_dimensions, right_type, right_dimensions, span)
|
||||||
}
|
}
|
||||||
|
(Type::Tuple(left_types), Type::Tuple(right_types)) => self.push_pairs_tuple(left_types, right_types, span),
|
||||||
(_, _) => Ok(()), // No `TypeVariable` found so we do not push any pairs.
|
(_, _) => Ok(()), // No `TypeVariable` found so we do not push any pairs.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1694,4 +1715,24 @@ impl TypeVariablePairs {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Checks if any given left or right tuple type contains a `TypeVariable`.
|
||||||
|
/// If a `TypeVariable` is found, create a new `TypeVariablePair` between the given left
|
||||||
|
/// and right type.
|
||||||
|
///
|
||||||
|
fn push_pairs_tuple(
|
||||||
|
&mut self,
|
||||||
|
left_types: &Vec<Type>,
|
||||||
|
right_types: &Vec<Type>,
|
||||||
|
span: &Span,
|
||||||
|
) -> Result<(), TypeAssertionError> {
|
||||||
|
// Iterate over each left == right pair of types.
|
||||||
|
for (left, right) in left_types.iter().zip(right_types) {
|
||||||
|
// Check for `TypeVariablePair`s.
|
||||||
|
self.push_pairs(left, right, span)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
use crate::{ScopeError, TypeAssertionError};
|
use crate::{ScopeError, TypeAssertionError};
|
||||||
use leo_static_check::TypeError;
|
use leo_static_check::TypeError;
|
||||||
use leo_typed::{Error as FormattedError, Span};
|
use leo_typed::{Error as FormattedError, Identifier, Span};
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -49,10 +49,55 @@ impl FrameError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///
|
///
|
||||||
// /// Return a new formatted error with a given message and span information
|
/// Return a new formatted error with a given message and span information
|
||||||
// ///
|
///
|
||||||
// fn new_from_span(message: String, span: Span) -> Self {
|
fn new_from_span(message: String, span: Span) -> Self {
|
||||||
// FrameError::Error(FormattedError::new_from_span(message, span))
|
FrameError::Error(FormattedError::new_from_span(message, span))
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Attempted to access the `Self` type outside of a circuit context.
|
||||||
|
///
|
||||||
|
pub fn circuit_self(span: &Span) -> Self {
|
||||||
|
let message = "The `Self` keyword is only valid inside a circuit context.".to_string();
|
||||||
|
|
||||||
|
Self::new_from_span(message, span.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Attempted to call non-static member using `::`.
|
||||||
|
///
|
||||||
|
pub fn invalid_member_access(identifier: &Identifier) -> Self {
|
||||||
|
let message = format!("non-static member `{}` must be accessed using `.` syntax", identifier);
|
||||||
|
|
||||||
|
Self::new_from_span(message, identifier.span.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Attempted to call static member using `.`.
|
||||||
|
///
|
||||||
|
pub fn invalid_static_access(identifier: &Identifier) -> Self {
|
||||||
|
let message = format!("static member `{}` must be accessed using `::` syntax", identifier);
|
||||||
|
|
||||||
|
Self::new_from_span(message, identifier.span.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Attempted to call a circuit type that is not defined in the current context.
|
||||||
|
///
|
||||||
|
pub fn undefined_circuit(identifier: &Identifier) -> Self {
|
||||||
|
let message = format!("The circuit `{}` is not defined.", identifier);
|
||||||
|
|
||||||
|
Self::new_from_span(message, identifier.span.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Attempted to call a circuit function that is not defined in the current context.
|
||||||
|
///
|
||||||
|
pub fn undefined_circuit_function(identifier: &Identifier) -> Self {
|
||||||
|
let message = format!("The circuit function `{}` is not defined.", identifier);
|
||||||
|
|
||||||
|
Self::new_from_span(message, identifier.span.to_owned())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,8 +46,12 @@ pub struct Identifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Identifier {
|
impl Identifier {
|
||||||
|
pub fn is_self_type(&self) -> bool {
|
||||||
|
self.name == "Self"
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_self(&self) -> bool {
|
pub fn is_self(&self) -> bool {
|
||||||
self.name == "Self" || self.name == "self"
|
self.is_self_type() || self.name == "self"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user