merge testnet3

This commit is contained in:
collin 2022-07-09 16:51:20 -07:00
commit a15fce710c
38 changed files with 306 additions and 240 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

@ -44,8 +44,8 @@ pub use tuple_init::*;
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)]
@ -64,7 +64,7 @@ pub enum Expression {
/// An identifier.
Identifier(Identifier),
/// A literal expression.
Literal(LiteralExpression),
Literal(Literal),
/// A ternary conditional expression `cond ? if_expr : else_expr`.
Ternary(TernaryExpression),
/// A tuple expression e.g., `(foo, 42, true)`.

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

@ -109,7 +109,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

@ -72,17 +72,6 @@ pub trait ExpressionVisitor<'a> {
Default::default()
}
fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}
fn visit_identifier(&mut self, _input: &'a Identifier, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}
fn visit_literal(&mut self, _input: &'a LiteralExpression, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}
fn visit_circuit_init(
&mut self,
_input: &'a CircuitExpression,
@ -91,6 +80,18 @@ pub trait ExpressionVisitor<'a> {
Default::default()
}
fn visit_err(&mut self, _input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}
fn visit_identifier(&mut self, _input: &'a Identifier, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}
fn visit_literal(&mut self, _input: &'a Literal, _additional: &Self::AdditionalInput) -> Self::Output {
Default::default()
}
fn visit_ternary(&mut self, input: &'a TernaryExpression, additional: &Self::AdditionalInput) -> Self::Output {
self.visit_expression(&input.condition, additional);
self.visit_expression(&input.if_true, additional);

View File

@ -53,6 +53,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

@ -120,7 +120,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

@ -384,9 +384,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()?;
@ -475,7 +473,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)
})?;
@ -511,44 +509,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::Identifier(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

@ -85,7 +85,7 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
// Check return type.
return Some(self.assert_and_return_type(core_instruction.return_type(), expected, access.span()));
} else {
self.emit_err(TypeCheckerError::invalid_access_expression(access, access.span()));
self.emit_err(TypeCheckerError::invalid_core_circuit_call(access, access.span()));
}
}
AccessExpression::Tuple(access) => {
@ -118,13 +118,199 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
self.emit_err(TypeCheckerError::type_should_be(type_, "tuple", access.span()));
}
}
self.emit_err(TypeCheckerError::invalid_core_circuit_call(access, access.span()));
}
}
_expr => {} // 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_)) => return Some(type_.clone()),
// Case where `access.name` is not a member of the circuit.
None => {
self.emit_err(TypeCheckerError::invalid_circuit_variable(
&access.name,
&circ,
access.name.span(),
));
}
}
} else {
self.emit_err(TypeCheckerError::invalid_circuit(&access.inner, access.inner.span()));
}
}
Some(type_) => {
self.emit_err(TypeCheckerError::type_should_be(type_, "circuit", access.inner.span()));
}
None => {
self.emit_err(TypeCheckerError::could_not_determine_type(
&access.inner,
access.inner.span(),
));
}
}
}
AccessExpression::AssociatedConstant(..) => {} // todo: Add support for associated constants (u8::MAX).
}
None
}
fn visit_circuit_init(
&mut self,
input: &'a CircuitExpression,
additional: &Self::AdditionalInput,
) -> Self::Output {
let circ = self.symbol_table.borrow().lookup_circuit(&input.name.name).cloned();
if let Some(circ) = circ {
// Check circuit type name.
let ret = self.check_expected_circuit(circ.identifier, additional, input.name.span());
// Check number of circuit members.
if circ.members.len() != input.members.len() {
self.emit_err(TypeCheckerError::incorrect_num_circuit_members(
circ.members.len(),
input.members.len(),
input.span(),
));
}
// Check circuit member types.
circ.members
.iter()
.for_each(|CircuitMember::CircuitVariable(name, ty)| {
// Lookup circuit variable name.
if let Some(actual) = input.members.iter().find(|member| member.identifier.name == name.name) {
if let Some(expr) = &actual.expression {
self.visit_expression(expr, &Some(ty.clone()));
}
} else {
self.emit_err(TypeCheckerError::missing_circuit_member(
circ.identifier,
name,
input.span(),
));
};
});
Some(ret)
} else {
self.emit_err(TypeCheckerError::unknown_sym(
"circuit",
&input.name.name,
input.name.span(),
));
None
}
}
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
if let Some(circuit) = self.symbol_table.borrow().lookup_circuit(&var.name) {
Some(self.assert_and_return_type(Type::Identifier(circuit.identifier), expected, var.span))
} else if let Some(var) = self.symbol_table.borrow().lookup_variable(&var.name) {
Some(self.assert_and_return_type(var.type_.clone(), expected, var.span))
} else {
self.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()));
None
}
}
fn visit_literal(&mut self, input: &'a Literal, expected: &Self::AdditionalInput) -> Self::Output {
Some(match input {
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 {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i8>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()));
}
}
IntegerType::I16 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i16>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()));
}
}
IntegerType::I32 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i32>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()));
}
}
IntegerType::I64 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i64>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()));
}
}
IntegerType::I128 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i128>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()));
}
}
IntegerType::U8 if str_content.parse::<u8>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span()))
}
IntegerType::U16 if str_content.parse::<u16>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span()))
}
IntegerType::U32 if str_content.parse::<u32>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span()))
}
IntegerType::U64 if str_content.parse::<u64>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span()))
}
IntegerType::U128 if str_content.parse::<u128>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u128", input.span()))
}
_ => {}
}
self.assert_and_return_type(Type::IntegerType(*type_), 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()),
})
}
fn visit_binary(&mut self, input: &'a BinaryExpression, destination: &Self::AdditionalInput) -> Self::Output {
match input.op {
BinaryOperation::And | BinaryOperation::Or | BinaryOperation::Nand | BinaryOperation::Nor => {
@ -350,13 +536,15 @@ 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.emit_err(TypeCheckerError::incorrect_num_args_to_call(
self.handler.emit_err(TypeCheckerError::incorrect_num_args_to_call(
func.input.len(),
input.arguments.len(),
input.span(),
@ -377,152 +565,11 @@ impl<'a> ExpressionVisitor<'a> for TypeChecker<'a> {
None
}
}
// TODO: Is this case sufficient?
expr => self.visit_expression(expr, expected),
}
}
fn visit_circuit_init(&mut self, input: &'a CircuitExpression, additional: &Self::AdditionalInput) -> Self::Output {
let circ = self.symbol_table.borrow().lookup_circuit(&input.name.name).cloned();
if let Some(circ) = circ {
// Check circuit type name.
let ret = self.check_expected_circuit(circ.identifier, additional, input.name.span());
// Check number of circuit members.
if circ.members.len() != input.members.len() {
self.emit_err(TypeCheckerError::incorrect_num_circuit_members(
circ.members.len(),
input.members.len(),
input.span(),
));
}
// Check circuit member types.
circ.members
.iter()
.for_each(|CircuitMember::CircuitVariable(name, ty)| {
// Lookup circuit variable name.
if let Some(actual) = input.members.iter().find(|member| member.identifier.name == name.name) {
if let Some(expr) = &actual.expression {
self.visit_expression(expr, &Some(ty.clone()));
}
} else {
self.emit_err(TypeCheckerError::unknown_sym(
"circuit member variable",
name,
name.span(),
));
};
});
Some(ret)
} else {
self.emit_err(TypeCheckerError::unknown_sym(
"circuit",
&input.name.name,
input.name.span(),
));
None
}
}
fn visit_identifier(&mut self, var: &'a Identifier, expected: &Self::AdditionalInput) -> Self::Output {
if let Some(circuit) = self.symbol_table.borrow().lookup_circuit(&var.name) {
Some(self.assert_and_return_type(Type::Identifier(circuit.identifier), expected, var.span))
} else if let Some(var) = self.symbol_table.borrow().lookup_variable(&var.name) {
Some(self.assert_and_return_type(var.type_.clone(), expected, var.span))
} else {
self.emit_err(TypeCheckerError::unknown_sym("variable", var.name, var.span()));
None
}
}
fn visit_literal(&mut self, input: &'a LiteralExpression, 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, _) => {
match type_ {
IntegerType::I8 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i8>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i8", input.span()));
}
}
IntegerType::I16 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i16>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i16", input.span()));
}
}
IntegerType::I32 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i32>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i32", input.span()));
}
}
IntegerType::I64 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i64>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i64", input.span()));
}
}
IntegerType::I128 => {
let int = if self.negate {
format!("-{str_content}")
} else {
str_content.clone()
};
if int.parse::<i128>().is_err() {
self.emit_err(TypeCheckerError::invalid_int_value(int, "i128", input.span()));
}
}
IntegerType::U8 if str_content.parse::<u8>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u8", input.span()))
}
IntegerType::U16 if str_content.parse::<u16>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u16", input.span()))
}
IntegerType::U32 if str_content.parse::<u32>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u32", input.span()))
}
IntegerType::U64 if str_content.parse::<u64>().is_err() => {
self.emit_err(TypeCheckerError::invalid_int_value(str_content, "u64", input.span()))
}
IntegerType::U128 if str_content.parse::<u128>().is_err() => {
self.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()),
})
}
fn visit_ternary(&mut self, input: &'a TernaryExpression, expected: &Self::AdditionalInput) -> Self::Output {
self.visit_expression(&input.condition, &Some(Type::Boolean));

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_.clone()));
self.check_core_type_conflict(&Some(input_var.type_.clone()));
// Check for conflicting variable names.
if let Err(err) = self.symbol_table.borrow_mut().insert_variable(

View File

@ -26,9 +26,9 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
// we can safely unwrap all self.parent instances because
// statements should always have some parent block
let parent = self.parent.unwrap();
let return_type = &self.symbol_table.borrow().lookup_fn(&parent).map(|f| f.output.clone());
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_.clone()));
self.check_core_type_conflict(&Some(input.type_.clone()));
self.visit_expression(&input.value, &Some(input.type_.clone()));
@ -71,9 +71,9 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
let var_type = if let Some(var) = self.symbol_table.borrow_mut().lookup_variable(&var_name.name) {
// TODO: Check where this check is moved to in `improved-flattening`.
match &var.declaration {
Declaration::Const => self.emit_err(TypeCheckerError::cannont_assign_to_const_var(var_name, var.span)),
Declaration::Const => self.emit_err(TypeCheckerError::cannot_assign_to_const_var(var_name, var.span)),
Declaration::Input(ParamMode::Const) => {
self.emit_err(TypeCheckerError::cannont_assign_to_const_input(var_name, var.span))
self.emit_err(TypeCheckerError::cannot_assign_to_const_input(var_name, var.span))
}
_ => {}
}
@ -86,7 +86,7 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
};
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);
}
}
@ -101,7 +101,8 @@ impl<'a> StatementVisitor<'a> for TypeChecker<'a> {
fn visit_iteration(&mut self, input: &'a IterationStatement) {
let iter_type = &Some(input.type_.clone());
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,
@ -132,10 +133,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 {
@ -149,8 +150,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

@ -85,8 +85,8 @@ impl<'a> TypeChecker<'a> {
}
/// Emits a type checker error.
pub fn emit_err(&self, err: TypeCheckerError) {
self.handler.emit_err(err.into());
pub(crate) fn emit_err(&self, err: TypeCheckerError) {
self.handler.emit_err(err);
}
/// Emits an error to the handler if the given type is invalid.
@ -311,7 +311,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: d040a3a4f2be00b8123a1cbdbfe08a651526c76f12b24e0378f4f11fa7ded820
- "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"

View File

@ -2,4 +2,4 @@
namespace: Compile
expectation: Fail
outputs:
- "Error [ETYC0372028]: Tuple index `2` out of range for a tuple with length `2`\n --> compiler-test:6:21\n |\n 6 | return (t.0, t.2); // Index `t.2` is out of bounds.\n | ^\n"
- "Error [ETYC0372030]: Tuple index `2` out of range for a tuple with length `2`\n --> compiler-test:6:21\n |\n 6 | return (t.0, t.2); // Index `t.2` is out of bounds.\n | ^\nError [ETYC0372020]: t.2 is not a valid core circuit call.\n --> compiler-test:6:21\n |\n 6 | return (t.0, t.2); // Index `t.2` is out of bounds.\n | ^\n"