add dynamic check errors for circuits 1

This commit is contained in:
collin 2020-10-24 02:53:09 -07:00
parent 40d26dce7f
commit 973e2a6afc
4 changed files with 126 additions and 45 deletions

View File

@ -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]

View File

@ -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(())
}
} }

View File

@ -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())
}
} }

View File

@ -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"
} }
} }