Merge pull request #1925 from AleoHQ/fix/tyc-member-access-and-cleanup

Type-checking fixes and cleanup
This commit is contained in:
d0cd 2022-07-09 16:03:53 -07:00 committed by GitHub
commit 1c88ea938b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 211 additions and 144 deletions

View File

@ -18,9 +18,9 @@ use crate::GroupLiteral;
use super::*;
/// A literal expression.
/// A literal.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum LiteralExpression {
pub enum Literal {
// todo: deserialize values here
/// An address literal, e.g., `aleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8s7pyjh9`.
Address(String, #[serde(with = "leo_span::span_json")] Span),
@ -41,7 +41,7 @@ pub enum LiteralExpression {
String(String, #[serde(with = "leo_span::span_json")] Span),
}
impl fmt::Display for LiteralExpression {
impl fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
Self::Address(address, _) => write!(f, "{}", address),
@ -55,7 +55,7 @@ impl fmt::Display for LiteralExpression {
}
}
impl Node for LiteralExpression {
impl Node for Literal {
fn span(&self) -> Span {
match &self {
Self::Address(_, span)

View File

@ -41,8 +41,8 @@ pub use ternary::*;
mod unary;
pub use unary::*;
mod value;
pub use value::*;
mod literal;
pub use literal::*;
/// Expression that evaluates to a value.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -52,7 +52,7 @@ pub enum Expression {
/// An identifier expression.
Identifier(Identifier),
/// A literal expression.
Literal(LiteralExpression),
Literal(Literal),
/// A binary expression, e.g., `42 + 24`.
Binary(BinaryExpression),
/// A call expression, e.g., `my_fun(args)`.

View File

@ -14,7 +14,7 @@
// 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/>.
use crate::{Expression, GroupLiteral, IntegerType, LiteralExpression, Node, Type, UnaryOperation};
use crate::{Expression, GroupLiteral, IntegerType, Literal, Node, Type, UnaryOperation};
use leo_errors::{InputError, LeoError, Result};
use serde::{Deserialize, Serialize};
@ -34,11 +34,11 @@ impl TryFrom<(Type, Expression)> for InputValue {
fn try_from(value: (Type, Expression)) -> Result<Self> {
Ok(match value {
(type_, Expression::Literal(lit)) => match (type_, lit) {
(Type::Address, LiteralExpression::Address(value, _)) => Self::Address(value),
(Type::Boolean, LiteralExpression::Boolean(value, _)) => Self::Boolean(value),
(Type::Field, LiteralExpression::Field(value, _)) => Self::Field(value),
(Type::Group, LiteralExpression::Group(value)) => Self::Group(*value),
(Type::IntegerType(expected), LiteralExpression::Integer(actual, value, span)) => {
(Type::Address, Literal::Address(value, _)) => Self::Address(value),
(Type::Boolean, Literal::Boolean(value, _)) => Self::Boolean(value),
(Type::Field, Literal::Field(value, _)) => Self::Field(value),
(Type::Group, Literal::Group(value)) => Self::Group(*value),
(Type::IntegerType(expected), Literal::Integer(actual, value, span)) => {
if expected == actual {
Self::Integer(expected, value)
} else {

View File

@ -42,7 +42,7 @@ pub trait ExpressionReconstructor {
(Expression::Identifier(input), Default::default())
}
fn reconstruct_literal(&mut self, input: LiteralExpression) -> (Expression, Self::AdditionalOutput) {
fn reconstruct_literal(&mut self, input: Literal) -> (Expression, Self::AdditionalOutput) {
(Expression::Literal(input), Default::default())
}

View File

@ -55,7 +55,7 @@ pub trait ExpressionVisitor<'a> {
Default::default()
}
fn visit_literal(&mut self, _input: &'a LiteralExpression, _additional: &Self::AdditionalInput) -> Self::Output {
fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}

View File

@ -51,6 +51,7 @@ impl Type {
///
/// Flattens array syntax: `[[u8; 1]; 2] == [u8; (2, 1)] == true`
///
// TODO: Does not seem to flatten?
pub fn eq_flat(&self, other: &Self) -> bool {
match (self, other) {
(Type::Address, Type::Address)

View File

@ -115,7 +115,7 @@ impl<'a> ParserContext<'a> {
/// Emit the error `err`.
pub(super) fn emit_err(&self, err: ParserError) {
self.handler.emit_err(err.into());
self.handler.emit_err(err);
}
/// Emit the error `err`.

View File

@ -374,9 +374,7 @@ impl ParserContext<'_> {
/// tuple initialization expression or an affine group literal.
fn parse_tuple_expression(&mut self) -> Result<Expression> {
if let Some(gt) = self.eat_group_partial().transpose()? {
return Ok(Expression::Literal(LiteralExpression::Group(Box::new(
GroupLiteral::Tuple(gt),
))));
return Ok(Expression::Literal(Literal::Group(Box::new(GroupLiteral::Tuple(gt)))));
}
let (mut tuple, trailing, span) = self.parse_expr_tuple()?;
@ -465,7 +463,7 @@ impl ParserContext<'_> {
/// Returns an [`Expression`] AST node if the next tokens represent a
/// circuit initialization expression.
/// let foo = Foo { x: 1u8 };
pub fn parse_circuit_expression(&mut self, identifier: Identifier) -> Result<Expression> {
pub fn parse_circuit_init_expression(&mut self, identifier: Identifier) -> Result<Expression> {
let (members, _, end) = self.parse_list(Delimiter::Brace, Some(Token::Comma), |p| {
p.parse_circuit_member().map(Some)
})?;
@ -501,44 +499,42 @@ impl ParserContext<'_> {
// Literal followed by `field`, e.g., `42field`.
Some(Token::Field) => {
assert_no_whitespace("field")?;
Expression::Literal(LiteralExpression::Field(value, full_span))
Expression::Literal(Literal::Field(value, full_span))
}
// Literal followed by `group`, e.g., `42group`.
Some(Token::Group) => {
assert_no_whitespace("group")?;
Expression::Literal(LiteralExpression::Group(Box::new(GroupLiteral::Single(
value, full_span,
))))
Expression::Literal(Literal::Group(Box::new(GroupLiteral::Single(value, full_span))))
}
// Literal followed by `scalar` e.g., `42scalar`.
Some(Token::Scalar) => {
assert_no_whitespace("scalar")?;
Expression::Literal(LiteralExpression::Scalar(value, full_span))
Expression::Literal(Literal::Scalar(value, full_span))
}
// Literal followed by other type suffix, e.g., `42u8`.
Some(suffix) => {
assert_no_whitespace(&suffix.to_string())?;
let int_ty = Self::token_to_int_type(suffix).expect("unknown int type token");
Expression::Literal(LiteralExpression::Integer(int_ty, value, full_span))
Expression::Literal(Literal::Integer(int_ty, value, full_span))
}
None => return Err(ParserError::implicit_values_not_allowed(value, span).into()),
}
}
Token::True => Expression::Literal(LiteralExpression::Boolean(true, span)),
Token::False => Expression::Literal(LiteralExpression::Boolean(false, span)),
Token::True => Expression::Literal(Literal::Boolean(true, span)),
Token::False => Expression::Literal(Literal::Boolean(false, span)),
Token::AddressLit(addr) => {
if addr.parse::<Address<Testnet2>>().is_err() {
self.emit_err(ParserError::invalid_address_lit(&addr, span));
}
Expression::Literal(LiteralExpression::Address(addr, span))
Expression::Literal(Literal::Address(addr, span))
}
Token::StaticString(value) => Expression::Literal(LiteralExpression::String(value, span)),
Token::StaticString(value) => Expression::Literal(Literal::String(value, span)),
Token::Ident(name) => {
let ident = Identifier { name, span };
if !self.disallow_circuit_construction && self.check(&Token::LeftCurly) {
// Parse circuit and records inits as circuit expressions.
// Enforce circuit or record type later at type checking.
self.parse_circuit_expression(ident)?
self.parse_circuit_init_expression(ident)?
} else {
Expression::Identifier(ident)
}

View File

@ -63,14 +63,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
if let Some(core_instruction) = self.check_core_circuit_call(&access.ty, &access.name) {
// Check num input arguments.
if core_instruction.num_args() != access.args.len() {
self.handler.emit_err(
TypeCheckerError::incorrect_num_args_to_call(
core_instruction.num_args(),
access.args.len(),
input.span(),
)
.into(),
);
self.handler.emit_err(TypeCheckerError::incorrect_num_args_to_call(
core_instruction.num_args(),
access.args.len(),
input.span(),
));
}
// Check first argument type.
@ -89,11 +86,56 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Some(self.assert_and_return_type(core_instruction.return_type(), expected, access.span()))
} else {
self.handler
.emit_err(TypeCheckerError::invalid_access_expression(access, access.span()).into());
.emit_err(TypeCheckerError::invalid_core_circuit_call(access, access.span()));
None
}
}
_expr => None, // todo: Add support for associated constants (u8::MAX).
AccessExpression::Member(access) => {
// Check that the type of `inner` in `inner.name` is a circuit.
match self.visit_expression(&access.inner, &None) {
Some(Type::Identifier(identifier)) => {
// Retrieve the circuit definition associated with `identifier`.
let circ = self.symbol_table.borrow().lookup_circuit(&identifier.name).cloned();
if let Some(circ) = circ {
// Check that `access.name` is a member of the circuit.
match circ
.members
.iter()
.find(|circuit_member| circuit_member.name() == access.name.name)
{
// Case where `access.name` is a member of the circuit.
Some(CircuitMember::CircuitVariable(_, type_)) => Some(*type_),
// Case where `access.name` is not a member of the circuit.
None => {
self.handler.emit_err(TypeCheckerError::invalid_circuit_variable(
&access.name,
&circ,
access.name.span(),
));
None
}
}
} else {
self.handler
.emit_err(TypeCheckerError::invalid_circuit(&access.inner, access.inner.span()));
None
}
}
Some(type_) => {
self.handler
.emit_err(TypeCheckerError::type_should_be(type_, "circuit", access.inner.span()));
None
}
None => {
self.handler.emit_err(TypeCheckerError::could_not_determine_type(
&access.inner,
access.inner.span(),
));
None
}
}
}
AccessExpression::AssociatedConstant(..) => None, // todo: Add support for associated constants (u8::MAX).
}
}
@ -109,14 +151,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
// Check number of circuit members.
if circ.members.len() != input.members.len() {
self.handler.emit_err(
TypeCheckerError::incorrect_num_circuit_members(
circ.members.len(),
input.members.len(),
input.span(),
)
.into(),
);
self.handler.emit_err(TypeCheckerError::incorrect_num_circuit_members(
circ.members.len(),
input.members.len(),
input.span(),
));
}
// Check circuit member types.
@ -129,16 +168,21 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
self.visit_expression(expr, &Some(*ty));
}
} else {
self.handler.emit_err(
TypeCheckerError::unknown_sym("circuit member variable", name, name.span()).into(),
);
self.handler.emit_err(TypeCheckerError::missing_circuit_member(
circ.identifier,
name,
input.span(),
));
};
});
Some(ret)
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("circuit", &input.name.name, input.name.span()).into());
self.handler.emit_err(TypeCheckerError::unknown_sym(
"circuit",
&input.name.name,
input.name.span(),
));
None
}
}
@ -150,17 +194,17 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Some(self.assert_and_return_type(var.type_, expected, var.span))
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()).into());
.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()));
None
}
}
fn visit_literal(&mut self, input: &'a LiteralExpression, expected: &Self::AdditionalInput) -> Self::Output {
fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::Output {
Some(match input {
LiteralExpression::Address(_, _) => self.assert_and_return_type(Type::Address, expected, input.span()),
LiteralExpression::Boolean(_, _) => self.assert_and_return_type(Type::Boolean, expected, input.span()),
LiteralExpression::Field(_, _) => self.assert_and_return_type(Type::Field, expected, input.span()),
LiteralExpression::Integer(type_, str_content, _) => {
Literal::Address(_, _) => self.assert_and_return_type(Type::Address, expected, input.span()),
Literal::Boolean(_, _) => self.assert_and_return_type(Type::Boolean, expected, input.span()),
Literal::Field(_, _) => self.assert_and_return_type(Type::Field, expected, input.span()),
Literal::Integer(type_, str_content, _) => {
match type_ {
IntegerType::I8 => {
let int = if self.negate {
@ -171,7 +215,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
if int.parse::<i8>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()).into());
.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()));
}
}
IntegerType::I16 => {
@ -183,7 +227,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
if int.parse::<i16>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()).into());
.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()));
}
}
IntegerType::I32 => {
@ -195,7 +239,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
if int.parse::<i32>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()).into());
.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()));
}
}
IntegerType::I64 => {
@ -207,7 +251,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
if int.parse::<i64>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()).into());
.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()));
}
}
IntegerType::I128 => {
@ -219,31 +263,31 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
if int.parse::<i128>().is_err() {
self.handler
.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()).into());
.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()));
}
}
IntegerType::U8 if str_content.parse::<u8>().is_err() => self
.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span()).into()),
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span())),
IntegerType::U16 if str_content.parse::<u16>().is_err() => self
.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span()).into()),
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span())),
IntegerType::U32 if str_content.parse::<u32>().is_err() => self
.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span()).into()),
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span())),
IntegerType::U64 if str_content.parse::<u64>().is_err() => self
.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span()).into()),
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span())),
IntegerType::U128 if str_content.parse::<u128>().is_err() => self
.handler
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span()).into()),
.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span())),
_ => {}
}
self.assert_and_return_type(Type::IntegerType(*type_), expected, input.span())
}
LiteralExpression::Group(_) => self.assert_and_return_type(Type::Group, expected, input.span()),
LiteralExpression::Scalar(_, _) => self.assert_and_return_type(Type::Scalar, expected, input.span()),
LiteralExpression::String(_, _) => self.assert_and_return_type(Type::String, expected, input.span()),
Literal::Group(_) => self.assert_and_return_type(Type::Group, expected, input.span()),
Literal::Scalar(_, _) => self.assert_and_return_type(Type::Scalar, expected, input.span()),
Literal::String(_, _) => self.assert_and_return_type(Type::String, expected, input.span()),
})
}
@ -403,7 +447,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
(Some(Type::Address), _) | (_, Some(Type::Address)) => {
// Emit an error for address comparison.
self.handler
.emit_err(TypeCheckerError::compare_address(input.op, input.span()).into());
.emit_err(TypeCheckerError::compare_address(input.op, input.span()));
}
(Some(Type::Field), t2) => {
// Assert rhs is field.
@ -533,20 +577,19 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
fn visit_call(&mut self, input: &'a CallExpression, expected: &Self::AdditionalInput) -> Self::Output {
match &*input.function {
Expression::Identifier(ident) => {
// Note: The function symbol lookup is performed outside of the `if let Some(func) ...` block to avoid a RefCell lifetime bug in Rust.
// Do not move it into the `if let Some(func) ...` block or it will keep `self.symbol_table` alive for the entire block and will be very memory inefficient!
let func = self.symbol_table.borrow().lookup_fn(&ident.name).cloned();
if let Some(func) = func {
let ret = self.assert_and_return_type(func.output, expected, func.span);
// Check number of function arguments.
if func.input.len() != input.arguments.len() {
self.handler.emit_err(
TypeCheckerError::incorrect_num_args_to_call(
func.input.len(),
input.arguments.len(),
input.span(),
)
.into(),
);
self.handler.emit_err(TypeCheckerError::incorrect_num_args_to_call(
func.input.len(),
input.arguments.len(),
input.span(),
));
}
// Check function argument types.
@ -560,10 +603,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
Some(ret)
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("function", &ident.name, ident.span()).into());
.emit_err(TypeCheckerError::unknown_sym("function", &ident.name, ident.span()));
None
}
}
// TODO: Is this case sufficient?
expr => self.visit_expression(expr, expected),
}
}

View File

@ -35,7 +35,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
self.parent = Some(input.name());
input.input.iter().for_each(|i| {
let input_var = i.get_variable();
self.check_ident_type(&Some(input_var.type_));
self.check_core_type_conflict(&Some(input_var.type_));
// Check for conflicting variable names.
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
@ -53,7 +53,7 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
if !self.has_return {
self.handler
.emit_err(TypeCheckerError::function_has_no_return(input.name(), input.span()).into());
.emit_err(TypeCheckerError::function_has_no_return(input.name(), input.span()));
}
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
@ -66,9 +66,9 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
let mut used = HashSet::new();
if !input.members.iter().all(|member| used.insert(member.name())) {
self.handler.emit_err(if input.is_record {
TypeCheckerError::duplicate_record_variable(input.name(), input.span()).into()
TypeCheckerError::duplicate_record_variable(input.name(), input.span())
} else {
TypeCheckerError::duplicate_circuit_member(input.name(), input.span()).into()
TypeCheckerError::duplicate_circuit_member(input.name(), input.span())
});
}
@ -81,12 +81,18 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
{
Some((_, actual_ty)) if expected_ty.eq_flat(actual_ty) => {} // All good, found + right type!
Some((field, _)) => {
self.handler
.emit_err(TypeCheckerError::record_var_wrong_type(field, expected_ty, input.span()).into());
self.handler.emit_err(TypeCheckerError::record_var_wrong_type(
field,
expected_ty,
input.span(),
));
}
None => {
self.handler
.emit_err(TypeCheckerError::required_record_variable(need, expected_ty, input.span()).into());
self.handler.emit_err(TypeCheckerError::required_record_variable(
need,
expected_ty,
input.span(),
));
}
};
check_has_field(sym::owner, Type::Address);

View File

@ -28,7 +28,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
let parent = self.parent.unwrap();
let return_type = &self.symbol_table.borrow().lookup_fn(&parent).map(|f| f.output);
self.check_ident_type(return_type);
self.check_core_type_conflict(return_type);
self.has_return = true;
self.visit_expression(&input.expression, return_type);
@ -42,7 +42,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
};
input.variable_names.iter().for_each(|v| {
self.check_ident_type(&Some(input.type_));
self.check_core_type_conflict(&Some(input.type_));
self.visit_expression(&input.value, &Some(input.type_));
@ -64,7 +64,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
Expression::Identifier(id) => id,
_ => {
self.handler
.emit_err(TypeCheckerError::invalid_assignment_target(input.place.span()).into());
.emit_err(TypeCheckerError::invalid_assignment_target(input.place.span()));
return;
}
};
@ -74,23 +74,22 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
match &var.declaration {
Declaration::Const => self
.handler
.emit_err(TypeCheckerError::cannont_assign_to_const_var(var_name, var.span).into()),
.emit_err(TypeCheckerError::cannot_assign_to_const_var(var_name, var.span)),
Declaration::Input(ParamMode::Const) => self
.handler
.emit_err(TypeCheckerError::cannont_assign_to_const_input(var_name, var.span).into()),
.emit_err(TypeCheckerError::cannot_assign_to_const_input(var_name, var.span)),
_ => {}
}
Some(var.type_)
} else {
self.handler
.emit_err(TypeCheckerError::unknown_sym("variable", var_name.name, var_name.span).into());
.emit_err(TypeCheckerError::unknown_sym("variable", var_name.name, var_name.span));
None
};
if var_type.is_some() {
self.check_ident_type(&var_type);
self.check_core_type_conflict(&var_type);
self.visit_expression(&input.value, &var_type);
}
}
@ -105,7 +104,8 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_iteration(&mut self, input: &'a IterationStatement) {
let iter_type = &Some(input.type_);
self.check_ident_type(iter_type);
self.assert_int_type(iter_type, input.variable.span);
self.check_core_type_conflict(iter_type);
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(
input.variable.name,
@ -136,10 +136,10 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_block(&mut self, input: &'a Block) {
// Creates a new sub-scope since we are in a block.
let scope_index = self.symbol_table.borrow_mut().insert_block();
let prev_st = std::mem::take(&mut self.symbol_table);
let previous_symbol_table = std::mem::take(&mut self.symbol_table);
self.symbol_table
.swap(prev_st.borrow().get_block_scope(scope_index).unwrap());
self.symbol_table.borrow_mut().parent = Some(Box::new(prev_st.into_inner()));
.swap(previous_symbol_table.borrow().get_block_scope(scope_index).unwrap());
self.symbol_table.borrow_mut().parent = Some(Box::new(previous_symbol_table.into_inner()));
input.statements.iter().for_each(|stmt| {
match stmt {
@ -153,8 +153,10 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
};
});
let prev_st = *self.symbol_table.borrow_mut().parent.take().unwrap();
self.symbol_table.swap(prev_st.get_block_scope(scope_index).unwrap());
self.symbol_table = RefCell::new(prev_st);
let previous_symbol_table = *self.symbol_table.borrow_mut().parent.take().unwrap();
// TODO: Is this swap necessary?
self.symbol_table
.swap(previous_symbol_table.get_block_scope(scope_index).unwrap());
self.symbol_table = RefCell::new(previous_symbol_table);
}
}

View File

@ -86,7 +86,7 @@ impl<'a> TypeChecker<'a> {
/// Emits a type checker error.
pub(crate) fn emit_err(&self, err: TypeCheckerError) {
self.handler.emit_err(err.into());
self.handler.emit_err(err);
}
/// Emits an error if the two given types are not equal.
@ -310,7 +310,7 @@ impl<'a> TypeChecker<'a> {
}
/// Emits an error if the given type conflicts with a core library type.
pub(crate) fn check_ident_type(&self, type_: &Option<Type>) {
pub(crate) fn check_core_type_conflict(&self, type_: &Option<Type>) {
// todo: deprecate this method.
if let Some(Type::Identifier(ident)) = type_ {
if self.account_types.contains(&ident.name) || self.algorithms_types.contains(&ident.name) {

View File

@ -205,8 +205,8 @@ impl Handler {
}
/// Emit the error `err`.
pub fn emit_err(&self, err: LeoError) {
self.inner.borrow_mut().emit_err(err);
pub fn emit_err<E: Into<LeoError>>(&self, err: E) {
self.inner.borrow_mut().emit_err(err.into());
}
/// Emit the error `err`.
@ -282,9 +282,9 @@ mod tests {
let res: Result<(), _> = Handler::with(|h| {
let s = Span::default();
assert_eq!(h.err_count(), 0);
h.emit_err(ParserError::invalid_import_list(s).into());
h.emit_err(ParserError::invalid_import_list(s));
assert_eq!(h.err_count(), 1);
h.emit_err(ParserError::unexpected_eof(s).into());
h.emit_err(ParserError::unexpected_eof(s));
assert_eq!(h.err_count(), 2);
Err(ParserError::spread_in_array_init(s).into())
});
@ -293,8 +293,8 @@ mod tests {
let res: Result<(), _> = Handler::with(|h| {
let s = Span::default();
h.emit_err(ParserError::invalid_import_list(s).into());
h.emit_err(ParserError::unexpected_eof(s).into());
h.emit_err(ParserError::invalid_import_list(s));
h.emit_err(ParserError::unexpected_eof(s));
Ok(())
});
assert_eq!(count_err(res.unwrap_err().to_string()), 2);

View File

@ -33,7 +33,7 @@ create_messages!(
/// For when the user tries to assign to a const input.
@formatted
cannont_assign_to_const_input {
cannot_assign_to_const_input {
args: (input: impl Display),
msg: format!(
"Cannot assign to const input `{input}`",
@ -43,7 +43,7 @@ create_messages!(
/// For when the user tries to assign to a const input.
@formatted
cannont_assign_to_const_var {
cannot_assign_to_const_var {
args: (var: impl Display),
msg: format!(
"Cannot assign to const variable `{var}`",
@ -61,6 +61,16 @@ create_messages!(
help: None,
}
/// For when the type checker cannot determine the type of an expression.
@formatted
could_not_determine_type {
args: (expr: impl Display),
msg: format!(
"Could not determine the type of `{expr}`",
),
help: None,
}
/// The method name is known but not supported for the given type.
@formatted
type_method_not_supported {
@ -201,12 +211,22 @@ create_messages!(
help: None,
}
/// For when the user is missing a circuit member during initialization.
@formatted
missing_circuit_member {
args: (circuit: impl Display, member: impl Display),
msg: format!(
"Circuit initialization expression for `{circuit}` is missing member `{member}`.",
),
help: None,
}
/// An invalid access call is made e.g., `bool::MAX`
@formatted
invalid_access_expression {
invalid_core_circuit_call {
args: (expr: impl Display),
msg: format!(
"Invalid method call to {expr}."
"{expr} is not a valid core circuit call."
),
help: None,
}

View File

@ -1,6 +1,6 @@
/*
namespace: Compile
expectation: Pass
expectation: Fail
inputs:
- inline.in: |
[main]

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372025]: Comparison `>` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x > sender;\n | ^^^^^^^^^^\n"
- "Error [ETYC0372027]: Comparison `>` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x > sender;\n | ^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372025]: Comparison `>=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x >= sender;\n | ^^^^^^^^^^^\n"
- "Error [ETYC0372027]: Comparison `>=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x >= sender;\n | ^^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372025]: Comparison `<` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x < sender;\n | ^^^^^^^^^^\n"
- "Error [ETYC0372027]: Comparison `<` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x < sender;\n | ^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372025]: Comparison `<=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x <= sender;\n | ^^^^^^^^^^^\n"
- "Error [ETYC0372027]: Comparison `<=` is not supported for the address type.\n --> compiler-test:6:12\n |\n 6 | return x <= sender;\n | ^^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372019]: Circuit Bar defined with more than one member with the same name.\n --> compiler-test:3:1\n |\n 3 | circuit Bar {\n 4 | x: u32,\n 5 | x: u32,\n 6 | }\n | ^\n"
- "Error [ETYC0372021]: Circuit Bar defined with more than one member with the same name.\n --> compiler-test:3:1\n |\n 3 | circuit Bar {\n 4 | x: u32,\n 5 | x: u32,\n 6 | }\n | ^\n"

View File

@ -1,7 +1,5 @@
---
namespace: Compile
expectation: Pass
expectation: Fail
outputs:
- output:
- initial_input_ast: no input
initial_ast: 9489ab9b78bd31ac516e1674a83c6b35708238174df88374a7b19cef3d4a8e8a
- "Error [ETYC0372006]: Unknown variable `b`\n --> compiler-test:10:13\n |\n 10 | return (b.x == a.x) == y;\n | ^\nError [ETYC0372004]: Could not determine the type of `b`\n --> compiler-test:10:13\n |\n 10 | return (b.x == a.x) == y;\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372014]: The type ComputeKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public compute_key: ComputeKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"
- "Error [ETYC0372015]: The type ComputeKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public compute_key: ComputeKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372014]: The type PrivateKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public private_key: PrivateKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"
- "Error [ETYC0372015]: The type PrivateKey is a reserved core type name.\n --> compiler-test:4:35\n |\n 4 | function main(public private_key: PrivateKey, a: bool) -> bool {\n | ^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372014]: The type Signature is a reserved core type name.\n --> compiler-test:4:33\n |\n 4 | function main(public signature: Signature, a: bool) -> bool {\n | ^^^^^^^^^\n"
- "Error [ETYC0372015]: The type Signature is a reserved core type name.\n --> compiler-test:4:33\n |\n 4 | function main(public signature: Signature, a: bool) -> bool {\n | ^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372014]: The type ViewKey is a reserved core type name.\n --> compiler-test:4:32\n |\n 4 | function main(public view_key: ViewKey, a: bool) -> bool {\n | ^^^^^^^\n"
- "Error [ETYC0372015]: The type ViewKey is a reserved core type name.\n --> compiler-test:4:32\n |\n 4 | function main(public view_key: ViewKey, a: bool) -> bool {\n | ^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372009]: Expected one type from `bool, i8, i16, i32, i64, u8, u16, u32, u64, string`, but got `u128`\n --> compiler-test:4:20\n |\n 4 | let a: group = Pedersen64::hash(1u128);\n | ^^^^^^^^^^^^^^^^^^^^^^^\n"
- "Error [ETYC0372010]: Expected one type from `bool, i8, i16, i32, i64, u8, u16, u32, u64, string`, but got `u128`\n --> compiler-test:4:20\n |\n 4 | let a: group = Pedersen64::hash(1u128);\n | ^^^^^^^^^^^^^^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372005]: Unknown variable `b`\n --> compiler-test:4:14\n |\n 4 | \tlet b: u8 = b;\n | ^\n"
- "Error [ETYC0372006]: Unknown variable `b`\n --> compiler-test:4:14\n |\n 4 | \tlet b: u8 = b;\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372015]: The function main has no return statement.\n --> compiler-test:3:1\n |\n 3 | function main() -> u8 {}\n | ^^^^^^^^^^^^^^^^^^^^^^^^\n"
- "Error [ETYC0372016]: The function main has no return statement.\n --> compiler-test:3:1\n |\n 3 | function main() -> u8 {}\n | ^^^^^^^^^^^^^^^^^^^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372009]: Expected one type from `scalar`, but got `group`\n --> compiler-test:4:26\n |\n 4 | return (_, _)group * a;\n | ^\n"
- "Error [ETYC0372010]: Expected one type from `scalar`, but got `group`\n --> compiler-test:4:26\n |\n 4 | return (_, _)group * a;\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372024]: The field `balance` in a `record` must have type `u64`.\n --> compiler-test:4:1\n |\n 4 | record Token {\n 5 | balance: address,\n 6 | owner: address,\n 7 | }\n | ^\n"
- "Error [ETYC0372026]: The field `balance` in a `record` must have type `u64`.\n --> compiler-test:4:1\n |\n 4 | record Token {\n 5 | balance: address,\n 6 | owner: address,\n 7 | }\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372020]: Record Token defined with more than one variable with the same name.\n --> compiler-test:3:1\n |\n 3 | record Token {\n 4 | // The token owner.\n 5 | owner: address,\n 6 | // The token owner.\n 7 | owner: address, // Cannot define two record variables with the same name.\n 8 | // The Aleo balance (in gates).\n 9 | balance: u64,\n 10 | // The token amount.\n 11 | amount: u64,\n 12 | }\n | ^\n"
- "Error [ETYC0372022]: Record Token defined with more than one variable with the same name.\n --> compiler-test:3:1\n |\n 3 | record Token {\n 4 | // The token owner.\n 5 | owner: address,\n 6 | // The token owner.\n 7 | owner: address, // Cannot define two record variables with the same name.\n 8 | // The Aleo balance (in gates).\n 9 | balance: u64,\n 10 | // The token amount.\n 11 | amount: u64,\n 12 | }\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372005]: Unknown circuit member variable `owner`\n --> compiler-test:5:5\n |\n 5 | owner: address,\n | ^^^^^\n"
- "Error [ETYC0372019]: Circuit initialization expression for `Token` is missing member `owner`.\n --> compiler-test:13:12\n |\n 13 | return Token {\n 14 | sender: r0, // This variable should be named `owner`.\n 15 | balance: 0u64,\n 16 | amount: r1,\n 17 | };\n | ^^^^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372023]: The `record` type requires the variable `owner: address`.\n --> compiler-test:4:1\n |\n 4 | record Token {\n 5 | // The Aleo balance (in gates).\n 6 | balance: u64,\n 7 | // The token amount.\n 8 | amount: u64,\n 9 | }\n | ^\n"
- "Error [ETYC0372025]: The `record` type requires the variable `owner: address`.\n --> compiler-test:4:1\n |\n 4 | record Token {\n 5 | // The Aleo balance (in gates).\n 6 | balance: u64,\n 7 | // The token amount.\n 8 | amount: u64,\n 9 | }\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372024]: The field `owner` in a `record` must have type `address`.\n --> compiler-test:4:1\n |\n 4 | record Token {\n 5 | balance: u64,\n 6 | owner: bool,\n 7 | }\n | ^\n"
- "Error [ETYC0372026]: The field `owner` in a `record` must have type `address`.\n --> compiler-test:4:1\n |\n 4 | record Token {\n 5 | balance: u64,\n 6 | owner: bool,\n 7 | }\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:4:20\n |\n 4 | let b: bool = -a == -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:4:26\n |\n 4 | let b: bool = -a == -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:5:20\n |\n 5 | let c: bool = -a > -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:5:25\n |\n 5 | let c: bool = -a > -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:6:20\n |\n 6 | let d: bool = -a < -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:6:25\n |\n 6 | let d: bool = -a < -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:7:20\n |\n 7 | let e: bool = -a >= -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:7:26\n |\n 7 | let e: bool = -a >= -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:8:20\n |\n 8 | let f: bool = -a <= -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:8:26\n |\n 8 | let f: bool = -a <= -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:9:18\n |\n 9 | let g: u8 = -a * -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:9:23\n |\n 9 | let g: u8 = -a * -1u8;\n | ^^^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:10:18\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^\nError [ETYC0372009]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:10:24\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^^^\n"
- "Error [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:4:20\n |\n 4 | let b: bool = -a == -1u8;\n | ^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:4:26\n |\n 4 | let b: bool = -a == -1u8;\n | ^^^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:5:20\n |\n 5 | let c: bool = -a > -1u8;\n | ^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:5:25\n |\n 5 | let c: bool = -a > -1u8;\n | ^^^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:6:20\n |\n 6 | let d: bool = -a < -1u8;\n | ^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:6:25\n |\n 6 | let d: bool = -a < -1u8;\n | ^^^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:7:20\n |\n 7 | let e: bool = -a >= -1u8;\n | ^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:7:26\n |\n 7 | let e: bool = -a >= -1u8;\n | ^^^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:8:20\n |\n 8 | let f: bool = -a <= -1u8;\n | ^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:8:26\n |\n 8 | let f: bool = -a <= -1u8;\n | ^^^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:9:18\n |\n 9 | let g: u8 = -a * -1u8;\n | ^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:9:23\n |\n 9 | let g: u8 = -a * -1u8;\n | ^^^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:10:18\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^\nError [ETYC0372010]: Expected one type from `field, group, i8, i16, i32, i64, i128`, but got `u8`\n --> compiler-test:10:24\n |\n 10 | let h: u8 = -a ** -1u8;\n | ^^^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372005]: Unknown variable `z`\n --> compiler-test:4:19\n |\n 4 | \tlet b: u8 = 1u8**z;\n | ^\n"
- "Error [ETYC0372006]: Unknown variable `z`\n --> compiler-test:4:19\n |\n 4 | \tlet b: u8 = 1u8**z;\n | ^\n"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372005]: Unknown variable `x`\n --> compiler-test:4:14\n |\n 4 | \tlet b: u8 = x*z;\n | ^\nError [ETYC0372005]: Unknown variable `z`\n --> compiler-test:4:16\n |\n 4 | \tlet b: u8 = x*z;\n | ^\n"
- "Error [ETYC0372006]: Unknown variable `x`\n --> compiler-test:4:14\n |\n 4 | \tlet b: u8 = x*z;\n | ^\nError [ETYC0372006]: Unknown variable `z`\n --> compiler-test:4:16\n |\n 4 | \tlet b: u8 = x*z;\n | ^\n"